在下面的代码中,用什么来避免复制、省略或右值引用以及移动构造函数?
std::string get(){return "...";}
void foo(std::string var){}
foo( get() ); //<--- here
最佳答案
std::string get(){
// this is similar to return std::string("..."), which is
// copied/moved into the return value object.
return "...";
}
RVO允许它把临时字符串对象直接构造成get()
的返回值对象。
foo( get() );
RVO允许它直接将临时字符串对象(返回值对象)直接构造成foo
的参数对象。
这些是允许的 RVO 场景。如果您的编译器无法应用它们,它必须使用移动构造函数(如果可用)将返回值分别移动到返回值对象和参数对象中。在这种情况下,这并不奇怪,因为无论如何,这两个临时对象都被视为右值。 (对于第一种情况,没有表达式对应于创建的临时对象,因此处理仅用于选择使用什么构造函数将临时对象复制/移动到返回值对象中)。
对于其他情况,编译器必须将事物视为右值,即使它们在其他情况下是左值
std::string get(){
std::string s = "...";
// this is similar to return move(s)
return s;
}
规范说当它可能根据它规定的规则将 RVO(或 NRVO)应用于左值时,实现需要将表达式视为右值并在可用时使用移动构造函数,并且仅当它不能时找到一个合适的构造函数,它应该使用表达式作为左值。程序员在这些情况下编写明确的移动是很遗憾的,因为很明显程序员总是想要一个移动而不是一个拷贝。
例子:
struct A { A(); A(A&); };
struct B { B(); B(B&&); };
A f() { A a; return a; }
B f() { B b; return b; }
首先,它将a
作为右值,但找不到接受此右值的构造函数(A&
无法绑定(bind)到右值)。因此,它再次将 a
视为原样(左值)。对于第二个,它将 b
作为右值,并让 B(B&&)
获取该右值并移动它。如果它将 b
作为左值(它是什么),那么复制初始化就会失败,因为 B
没有隐式声明复制构造函数。
注意返回和传参使用的是复制初始化的规则,也就是说
u -> T (where u's type is different from T) =>
T rvalue_tmp = u;
T target(rvalue_tmp);
t -> T (where t's type is T) =>
T target = t;
因此,在我们返回 "..."
的示例中,我们首先创建一个临时右值,然后将其移动到目标中。对于我们返回返回值/参数类型的表达式的情况,我们会直接将表达式移动/复制到目标中。
关于c++ - 复制省略和右值引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5674983/