我应该移动 std::exchange ed 成员吗?

Usi*_*Cpp 3 c++ move-constructor c++14

std::exchange可用于impliment移动构造函数。这是来自 cppreference.com https://en.cppreference.com/w/cpp/utility/exchange#Notes的示例。

但是,可能的实现std::exchange如下所示:

template<class T, class U = T>
T exchange(T& obj, U&& new_value)
{
    T old_value = std::move(obj);
    obj = std::forward<U>(new_value);
    return old_value;          // can be copy (until C++17) or a move (C++17), right?
}
Run Code Online (Sandbox Code Playgroud)

现在我的情况:

#include <string>
#include <utility>

struct MyClass
{
    std::string m_str;
    // some other non-primitive members of the class

    MyClass(MyClass&& other) : m_str{ std::exchange(other.m_str, {}) } // enough?
        // or
        // : m_str{ std::move(std::exchange(other.m_str, {})) }
        //          ^^^^^^^^^^    do i need to move?                          
    {}

    MyClass& operator=(MyClass&& other)
    {
        this->m_str = std::exchange(other.m_str, {}); // enough?
        // or 
        // this->m_str = std::move( std::exchange(other.m_str, {}) );
        //               ^^^^^^^^^^    do I need to move?   
        return *this;
    }
};
Run Code Online (Sandbox Code Playgroud)

当我评论代码时,有机会按行移动或复制

m_str{ std::exchange(other.m_str, {}) }
this->m_str = std::exchange(other.m_str, nullptr);
Run Code Online (Sandbox Code Playgroud)

所以,

  • 我应该明确地std::move为他们使用,以便我可以确保成员已 100% 移动到other对象?
  • 如果Yesstd::exchange在这种情况下使用会更冗长吗?

我正在使用带有编译器标志 C++14 的 Visual Studio 2017。

bar*_*top 5

不,std::move这里不需要使用。经验法则是 - 如果某些返回值未分配给变量,它将被移动。

template<class T, class U = T>
T exchange(T& obj, U&& new_value)
{
    T old_value = std::move(obj);
    obj = std::forward<U>(new_value);
    return old_value;          // will be moved if move constructor defined
    // or even copy will be elided and will be no constructor call
}
Run Code Online (Sandbox Code Playgroud)

此举在这里保证与您所说的相反。C++17 更改了复制省略规则,但这是不同的

这里你可以看到 prvalue 是:

函数调用或重载的运算符表达式,其返回类型为非引用,如 str.substr(1, 2)、str1 + str2 或 it++

纯右值的属性(作为右值的子集)是(强调我的):

右值可用于初始化右值引用,在这种情况下,由右值标识的对象的生命周期会延长,直到引用的范围结束。

当用作函数参数并且当函数的两个重载可用时,一个采用右值引用参数,另一个采用左值引用 const 参数,右值绑定到右值引用重载(因此,如果复制和移动构造函数都可用,右值参数调用移动构造函数,同样使用复制和移动赋值运算符)。