C++ 存在RVO时默认/删除移动构造函数和赋值
我想这个问题已经被问过了,但我还没有找到类似的答案 让我们看一个人为的例子C++ 存在RVO时默认/删除移动构造函数和赋值,c++,c++11,return-value,rvalue-reference,return-value-optimization,C++,C++11,Return Value,Rvalue Reference,Return Value Optimization,我想这个问题已经被问过了,但我还没有找到类似的答案 让我们看一个人为的例子 #include <iostream> #include <string> #include <cassert> #define LOG \ std::cout << __PRETTY_FUNCTION__ << ' ' << str_ << '\t' << this << std::endl; c
#include <iostream>
#include <string>
#include <cassert>
#define LOG \
std::cout << __PRETTY_FUNCTION__ << ' ' << str_ << '\t' << this << std::endl;
class Test {
public:
Test(std::string const &str) : str_(str) { LOG; }
Test(Test const &rhs) : str_(rhs.str_) { LOG; }
// Test(Test &&rhs) = delete;
Test(Test &&rhs) : str_(std::move(rhs.str_)) { LOG; }
// Test &operator=(Test const &rhs) {
// if (this == &rhs) return this;
// str_ = rhs.str_;
// LOG;
// return *this;
// }
// Test &operator=(Test &&rhs) = delete;
// Test &operator=(Test &&rhs) {
// assert(this != &rhs);
// str_.swap(rhs.str_);
// LOG;
// return *this;
// }
~Test() { LOG; }
static Test gen() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
return Test("DUMMY");
}
private:
std::string str_;
};
int main(void) {
{
Test test = Test("test");
Test t(test);
}
std::cout << std::endl;
{ Test t0(Test("t0")); }
std::cout << std::endl;
{
Test t1 = Test{"t1"};
/// t1 = Test("t2");
}
std::cout << std::endl;
{ Test t(Test::gen()); }
return 0;
}
当我像src中那样定制move构造函数时,它会编译。然而
但是,甚至没有调用移动构造函数(结果如下):
后来我发现它可能是由于返回值优化
造成的,所以我用-fno-elide构造函数重新编译了它。这一次的结果如下:
Test::Test(const std::string &) test 0x7fff9590cd90
Test::Test(Test &&) test 0x7fff9590cd98
Test::~Test() 0x7fff9590cd90
Test::Test(const Test &) test 0x7fff9590cd78
Test::~Test() test 0x7fff9590cd78
Test::~Test() test 0x7fff9590cd98
Test::Test(const std::string &) t0 0x7fff9590cd68
Test::Test(Test &&) t0 0x7fff9590cd70
Test::~Test() 0x7fff9590cd68
Test::~Test() t0 0x7fff9590cd70
Test::Test(const std::string &) t1 0x7fff9590cd48
Test::Test(Test &&) t1 0x7fff9590cd50
Test::~Test() 0x7fff9590cd48
Test::~Test() t1 0x7fff9590cd50
static Test Test::gen()
Test::Test(const std::string &) DUMMY 0x7fff9590ccf0
Test::Test(Test &&) DUMMY 0x7fff9590cd28
Test::~Test() 0x7fff9590ccf0
Test::Test(Test &&) DUMMY 0x7fff9590cd30
Test::~Test() 0x7fff9590cd28
Test::~Test() DUMMY 0x7fff9590cd30
它按预期调用了移动构造函数。但是,显式删除move构造函数仍然会导致程序无法编译
我的问题是:
- 当删除
移动构造函数时,为什么会报告错误为什么它没有匹配复制构造函数
(虽然没有与移动构造函数
完全匹配)?C++03
没有右值参考,那么编译器的解决方案是什么?此外,我阅读并假设,在我的例子中,由于我指定了一个用户声明的复制构造函数,所以移动构造函数不应该是默认的(因此它应该被删除?),我也意识到有类似的描述。叮当声符合标准吗
返回值优化在这里做了什么?特别是,为什么调用Test::Test(const Test&)
而不是Test::Test(Test&)
?(很抱歉,我没有注意到RVO版本结果中只有一个副本构造函数调用)
- 我还注意到
移动分配
中存在类似问题(如果删除,则编译投诉错误)。这是怎么回事
即使编译器省略了复制或移动,该语言仍然要求函数存在并可访问。这是为了使程序始终一致地编译,而不管编译器是否删除特定的复制/移动
test
有一个名称,因此它会自动成为左值,如果没有显式的std::move
,就不能从中移动。因此,编译器必须将test
复制到t
中
我不明白您期望发生什么-我看不出它为什么不调用移动分配操作符
简短的版本是,如果在优化之前名义上调用了move或copy构造函数,那么它们必须存在并且可以访问。这正是语言标准的要求。删除的特殊成员函数参与重载解析,并且需要移动或复制构造函数,即使RVO省略了副本。如果提供了复制构造函数但没有移动构造函数,则不会删除移动构造函数。它根本没有声明。无论是否执行了优化,在没有进行优化的情况下,程序都必须有效。@HongxuChen:不,不能调用既不存在也不被称为存在的函数。@HongxuChen:不,代码中从来没有提到过未声明的函数,它以任何方式、形状或形式都不存在,时期它不存在。deleted
函数“存在”,但在您尝试使用它时会报告错误。(你是对的,它非常奇怪和愚蠢,我认为已删除的名称很糟糕。)1。我在理解标准中的move构造函数
implicit rule`时仍然有问题。是否会为RVO版本生成此默认移动构造函数?2。只需删除问题的第二部分。对不起,我弄错了。这种情况类似于构造函数,当删除move-assignment
时,编译器会发出错误。基本上我不知道为什么move-constructor/assignment
应该存在,即使最后没有调用它们?确切地说,move-constructor何时将从生成的类中删除?
Test::Test(const std::string &) test 0x7fff2e513448
Test::Test(const Test &) test 0x7fff2e513430
Test::~Test() test 0x7fff2e513430
Test::~Test() test 0x7fff2e513448
Test::Test(const std::string &) t0 0x7fff2e513428
Test::~Test() t0 0x7fff2e513428
Test::Test(const std::string &) t1 0x7fff2e513410
Test::~Test() t1 0x7fff2e513410
static Test Test::gen()
Test::Test(const std::string &) DUMMY 0x7fff2e5133f8
Test::~Test() DUMMY 0x7fff2e5133f8
Test::Test(const std::string &) test 0x7fff9590cd90
Test::Test(Test &&) test 0x7fff9590cd98
Test::~Test() 0x7fff9590cd90
Test::Test(const Test &) test 0x7fff9590cd78
Test::~Test() test 0x7fff9590cd78
Test::~Test() test 0x7fff9590cd98
Test::Test(const std::string &) t0 0x7fff9590cd68
Test::Test(Test &&) t0 0x7fff9590cd70
Test::~Test() 0x7fff9590cd68
Test::~Test() t0 0x7fff9590cd70
Test::Test(const std::string &) t1 0x7fff9590cd48
Test::Test(Test &&) t1 0x7fff9590cd50
Test::~Test() 0x7fff9590cd48
Test::~Test() t1 0x7fff9590cd50
static Test Test::gen()
Test::Test(const std::string &) DUMMY 0x7fff9590ccf0
Test::Test(Test &&) DUMMY 0x7fff9590cd28
Test::~Test() 0x7fff9590ccf0
Test::Test(Test &&) DUMMY 0x7fff9590cd30
Test::~Test() 0x7fff9590cd28
Test::~Test() DUMMY 0x7fff9590cd30