C++ 是C++;移动构造函数是否过时?

C++ 是C++;移动构造函数是否过时?,c++,move-semantics,C++,Move Semantics,我编写了自己的字符串类型(Str)来演示基本的构造函数、析构函数和赋值运算符;而且,除了move构造函数,我可以看到它们都在C++17中执行 显然,由于返回值优化(RVO),move构造函数不再被广泛使用 是否仅在显式调用std::move时调用move构造函数 什么时候可以叫它? 它是否因为RVO而过时 这是我的Str类型: struct Str { Str(): p(nullptr) {} Str(const char* s) { cout << "cvctor \""

我编写了自己的字符串类型(
Str
)来演示基本的构造函数、析构函数和赋值运算符;而且,除了move构造函数,我可以看到它们都在C++17中执行

显然,由于返回值优化(RVO),move构造函数不再被广泛使用

是否仅在显式调用
std::move
时调用move构造函数

什么时候可以叫它?
它是否因为RVO而过时

这是我的
Str
类型:

struct Str {
  Str(): p(nullptr) {}
  Str(const char* s) { cout << "cvctor \"" << s << "\"\n"; copy(s); }
  Str(const Str& s): p(nullptr) { cout << "cpctor deep\""<<s.p<<"\"\n"; copy(s.p); }
  Str( Str&& s) { cout << "mvctr shallow \"" << s.p << "\"\n"; p = s.p; s.p=nullptr; }
  const Str& operator=(const Str& s) { cout << "op=\""<<s.p<<"\"\n"; copy(s.p); return *this; }
  const Str& operator=( Str&& s) { cout << "op= shallow \""<<s.p<<"\"\n"; p=s.p; s.p=nullptr; return *this; }
  ~Str(){ if ( p != nullptr ) { cout << "dtor \"" << p << "\"\n"; delete [] p; } }

private:
  char* p = nullptr;
  char* copy(const char* s)
};
结构Str{ Str():p(nullptr){} Str(const char*s){完全不能 返回值优化不是使用移动构造函数的唯一点。每次要从
rvalue
构造某种类型的值时,都会使用移动构造函数

你基本上一次问两个问题。让我们从

是否只在显式调用std::move时调用move构造函数

move构造函数和
std::move
是相切的,但本质上是非常独立的。每次从同一类型的
rvalue
初始化变量时,都会调用move构造函数。另一方面
std::move
可以显式地从所谓的
左值
到这样的变量e> 右值,但这不是唯一的方法

template<typename T>
void foo(T&& value) { // A so-called universal reference
    T other_value = std::forward<T>(value);
}
foo( string{"some string"} ); // other_value is move initialized
思考
&
的正确方法是,这样的引用可以用
rvalue
初始化,但它本身并不总是这样的
rvalue
。有关更多信息,请参阅


它是否因为RVO而过时

两个值得注意的例子是,除了RVO之外,move构造函数还经常被使用

  • 进入方法参数

    void foo(string value);
    // Somewhere else
    string& some_string = get_me_a_string();
    foo( ::std::move(some_string) ); // Uses move constructor to initialize value
    some_string.clear(); // Most probably a no-op
                        // Doing this leaves an empty some_string
    
    请注意,在上面的示例中,
    some_string
    是一个引用这一事实与是否使用move构造无关。它是一个引用,用于明确RVO可能并不总是可能的。在这种情况下,在从
    some_string
    移动之后,将处于未指定但有效的状态,这是一种奇特的方式表示不会发生未定义的行为,并且引用仍然有效

  • 进入农田

    class FooBar {
        string fooField;
        //Constructor
        FooBar( string bar )
        : fooField( ::std::move(bar) ) // Uses move constructor to initialize fooField
        { }
    }
    

尝试
向后推几次
std::vector
@Nemo说的话,除了然后将
noexcept
添加到移动构造函数中,然后再次尝试向量测试。有趣!为什么noexcept会有这么大的不同?问题是关于对
std::move
@AlexeyAndronov的隐式调用。我想你是指隐式调用移动构造函数?没有对
std::move
的隐式调用。啊哈!多个条件返回语句将阻止RVO并触发移动构造函数调用,如下所述:
class FooBar {
    string fooField;
    //Constructor
    FooBar( string bar )
    : fooField( ::std::move(bar) ) // Uses move constructor to initialize fooField
    { }
}