Return 无法按值复制的对象
Return object that cannot be copied by value
我有一个对象,其复制操作太慢,所以我决定 delete
它并强制用户只能移动。这个对象的副本无论如何都没有多大意义。但是后来我有这个功能:
Object loadFromFile(const std::string& name) {
Object obj;
...
return obj;
}
即使这里发生了复制省略并且没有调用复制构造函数,这也无法编译,因为复制构造函数需要存在并且可以访问。这是我的第二次尝试:
Object&& loadFromFile(const std::string& name) {
Object obj;
...
return std::move(obj);
}
编译通过。耶!
但是在尝试使用它时出现了一个新问题:
Object x = loadFromFile("test.txt");
这又需要一个复制构造函数。即使明确使用 move:
我也无法让它工作
Object x = std::move(loadFromFile("test.txt"));
我唯一的解决方案是:
const Object& x = loadFromFile("test.txt");
但是 x
必须是非常量,因为它稍后会被更改。
如何处理?
你能把它作为输出参数传递吗?
类似于:
void loadFromFile(const std::string& name, Object& obj) {
//Operations on obj
}
哎呀,我错了,抱歉。
我删除了复制构造函数,但实际上并没有实施移动,假设它已经存在。创建一个移动构造函数解决了这个问题。
感谢@Joseph Mansfield 的点亮。
这个错误:
Object&& loadFromFile(const std::string& name) {
Object obj;
...
return std::move(obj);
}
您正在return引用局部变量,这是未定义的行为。对象死了,你 return 是对你鼻子的引用,所以恶魔可以从它出来。
第二个正确:
Object loadFromFile(const std::string& name) {
Object obj;
...
return obj;
}
确实,在这种情况下,首先执行查找,就好像 obj
是一个右值(标准 12.8.32):
When the criteria for elision of a copy/move operation are met, but not for an exception-declaration, and the object to be copied is designated by an lvalue, or when the expression in a return statement is a (possibly parenthesized) id-expression that names an object with automatic storage duration declared in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. If the first overload resolution fails or was not performed, or if the type of the first parameter of the selected constructor is not an rvalue reference to the object’s type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue. [ Note: This two-stage overload resolution must be performed regardless of whether copy elision will occur. It determines the constructor to be called if elision is not performed, and the selected constructor must be accessible even if the call is elided. —end note ]
并且应该选择移动构造函数Object(Object&&)
。
我有一个对象,其复制操作太慢,所以我决定 delete
它并强制用户只能移动。这个对象的副本无论如何都没有多大意义。但是后来我有这个功能:
Object loadFromFile(const std::string& name) {
Object obj;
...
return obj;
}
即使这里发生了复制省略并且没有调用复制构造函数,这也无法编译,因为复制构造函数需要存在并且可以访问。这是我的第二次尝试:
Object&& loadFromFile(const std::string& name) {
Object obj;
...
return std::move(obj);
}
编译通过。耶!
但是在尝试使用它时出现了一个新问题:
Object x = loadFromFile("test.txt");
这又需要一个复制构造函数。即使明确使用 move:
我也无法让它工作Object x = std::move(loadFromFile("test.txt"));
我唯一的解决方案是:
const Object& x = loadFromFile("test.txt");
但是 x
必须是非常量,因为它稍后会被更改。
如何处理?
你能把它作为输出参数传递吗? 类似于:
void loadFromFile(const std::string& name, Object& obj) {
//Operations on obj
}
哎呀,我错了,抱歉。
我删除了复制构造函数,但实际上并没有实施移动,假设它已经存在。创建一个移动构造函数解决了这个问题。
感谢@Joseph Mansfield 的点亮。
这个错误:
Object&& loadFromFile(const std::string& name) {
Object obj;
...
return std::move(obj);
}
您正在return引用局部变量,这是未定义的行为。对象死了,你 return 是对你鼻子的引用,所以恶魔可以从它出来。
第二个正确:
Object loadFromFile(const std::string& name) {
Object obj;
...
return obj;
}
确实,在这种情况下,首先执行查找,就好像 obj
是一个右值(标准 12.8.32):
When the criteria for elision of a copy/move operation are met, but not for an exception-declaration, and the object to be copied is designated by an lvalue, or when the expression in a return statement is a (possibly parenthesized) id-expression that names an object with automatic storage duration declared in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. If the first overload resolution fails or was not performed, or if the type of the first parameter of the selected constructor is not an rvalue reference to the object’s type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue. [ Note: This two-stage overload resolution must be performed regardless of whether copy elision will occur. It determines the constructor to be called if elision is not performed, and the selected constructor must be accessible even if the call is elided. —end note ]
并且应该选择移动构造函数Object(Object&&)
。