c++ - 复制省略和右值引用

标签 c++ c++11

在下面的代码中,用什么来避免复制、省略或右值引用以及移动构造函数?

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/

相关文章:

c++ - unique_ptr 之外的数组模板语法

c++ - 标准布局和继承

c++ - 修复基于模块的库中的自阻塞包含

c++ - 变体实现(可变参数模板)C++

C++ OpenGL glCreateProgram 在 Windows 上返回 0

c++ - 如何在 C++ 中打印出以下实现的链表?

c++ - C++ 模板是否能够从父类获取 "forward any class function"?

c++ - 为什么初始化列表不能用作参数?

c++ - 如何使用gdb调试GUI程序

c++ - 使用 reed solomon 完全恢复数据