C++ 将右值指定为返回值时出现分段错误
我有一个构建新对象的工厂类新对象不应复制,但可以移动。因此我想在提供移动构造函数和移动赋值操作符的同时删除复制构造函数和复制赋值操作符 我想,为了帮助传达对象必须移动到位而不是复制到位的想法,我将返回一个右值引用。但是,在执行此操作时,编译器似乎总是生成代码,销毁返回的“过期”对象,然后将相同(已销毁!)对象提供给移动构造函数或移动赋值运算符 所以我想知道;标准中是否有我违反的地方?如果是,是否有警告(最好是错误)可以阻止我继续做如此愚蠢的事情?或者,如果我没有违反任何标准,那么我假设这是一个编译器错误C++ 将右值指定为返回值时出现分段错误,c++,c++11,gcc,clang,rvalue-reference,C++,C++11,Gcc,Clang,Rvalue Reference,我有一个构建新对象的工厂类新对象不应复制,但可以移动。因此我想在提供移动构造函数和移动赋值操作符的同时删除复制构造函数和复制赋值操作符 我想,为了帮助传达对象必须移动到位而不是复制到位的想法,我将返回一个右值引用。但是,在执行此操作时,编译器似乎总是生成代码,销毁返回的“过期”对象,然后将相同(已销毁!)对象提供给移动构造函数或移动赋值运算符 所以我想知道;标准中是否有我违反的地方?如果是,是否有警告(最好是错误)可以阻止我继续做如此愚蠢的事情?或者,如果我没有违反任何标准,那么我假设这是一个编
// g++ (Ubuntu 4.9.2-0ubuntu1~14.04) 4.9.2
// g++ test.cpp -std=c++11
#include <iostream>
#include <memory>
struct PTR {
char * blah = nullptr;
PTR(char* blah) : blah(blah)
{
std::cout << "\tctor @" << (void*)this << std::endl;
}
PTR(const PTR & copy_ctor) : blah(new char)
{
*blah = *copy_ctor.blah;
std::cout << "\tcopy @@" << (void*)this << "\t<-" << (void*)©_ctor << std::endl;
}
PTR(PTR && move_ctor) : blah(move_ctor.blah)
{
move_ctor.blah = nullptr;
std::cout << "\tctor&&@" << (void*)this << "\t<@" << (void*)&move_ctor << std::endl;
}
PTR & operator=(const PTR & copy_assign)
{ delete blah;
blah = new char;
*blah = *copy_assign.blah;
std::cout << "copyas@" << (void*)this << "\t<-" << (void*)©_assign << std::endl;
return *this;
}
PTR & operator=(PTR && move_assign)
{
delete blah;
blah = move_assign.blah;
move_assign.blah = nullptr;
std::cout << "\tmove&&@" << (void*)this << "\t<@" << (void*)&move_assign << std::endl;
return *this;
}
~PTR()
{
std::cout << "\tdtor~~@" << (void*)this << std::endl;
delete blah;
}
};
PTR make_ptr_l() {
PTR ptr(new char());
return std::move(ptr); // Without std::move, compiler *may* opt to copy the class, which is undesired
}
PTR && make_ptr_r() {
PTR ptr(new char());
return std::move(ptr); // Requires std::move to turn ptr into rvalue, otherwise compiler error
}
int main() {
std::cout << "lvalue: \n" << std::flush;
PTR ptr = make_ptr_l();
std::cout << "successful\nrvalue new: \n" << std::flush;
{
PTR ptr_r = make_ptr_r();
std::cout << "successful\nrvalue assign: \n" << std::flush;
}
ptr = make_ptr_r();
std::cout << "successful" << std::endl;
return 0;
}
//g++(Ubuntu 4.9.2-0ubuntu1~14.04)4.9.2
//g++test.cpp-std=c++11
#包括
#包括
结构PTR{
char*blah=nullptr;
PTR(字符*废话):废话(废话)
{
std::cout您正在返回对临时文件的引用。您可以从输出中看出这一点:
rvalue new:
ctor @0x7ffed71b7a00
dtor~~@0x7ffed71b7a00
ctor&&@0x7ffed71b7a40 <@0x7ffed71b7a00
这就是为什么make_ptr_l
可以工作的原因-您返回的是一个值,而不是一个引用
没有必要。make_ptr_r
返回对局部变量的引用。当函数退出时,该变量消失,引用变为悬空。任何实际使用该引用的尝试都会显示未定义的行为。这是一个右值引用的事实不会改变任何内容。要添加到@IgorTandetnik said:make\ptr\r
的返回类型应该是ptr
,不带&&
。检查代码的输出,看看它对您是否有意义。@DanielFrey我检查了输出,甚至提到它没有多大意义。我误解的是std::move()的想法如果返回类型为右值引用,则应将对象移出本地。@inetknght抱歉,无意冒犯!我指的是在您按照我的建议更改返回类型并将其与以前的输出进行比较后的输出。我希望这种差异将有助于您理解,即使没有返回类型上的&&
,您的移动和移动也会受到影响-赋值将是正确的,并有助于减少复制操作。std::move
不会移动任何东西。这只是将对象强制转换为右值引用的一种方便方法:std::move(x)
大致相当于static\u cast(x)
。其他东西(例如移动构造函数)现在可以将此引用作为参数,并将数据移出x
。因此,本例中的右值引用不会像我理解的那样移动对象?@inetknghtstd::move()
不会移动。它只是一个强制转换。
rvalue new:
ctor @0x7ffed71b7a00
dtor~~@0x7ffed71b7a00
ctor&&@0x7ffed71b7a40 <@0x7ffed71b7a00
T& foo() {
T object;
return object;
}