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

Should I move the std::exchange ed members?

std::exchange可用于实现移动构造函数。这是来自 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?
}

现在我的情况:

#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;
    }
};

正如我对代码的评论,有机会按行移动或复制

m_str{ std::exchange(other.m_str, {}) }
this->m_str = std::exchange(other.m_str, nullptr);

因此,

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

不,这里不需要使用 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
}

这里保证和你说的相反。 C++17 改变了复制省略规则,但这是不同的

here可以看出prvalue是:

function call or an overloaded operator expression, whose return type is non-reference, such as str.substr(1, 2), str1 + str2, or it++

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

An rvalue may be used to initialize an rvalue reference, in which case the lifetime of the object identified by the rvalue is extended until the scope of the reference ends.

When used as a function argument and when two overloads of the function are available, one taking rvalue reference parameter and the other taking lvalue reference to const parameter, an rvalue binds to the rvalue reference overload (thus, if both copy and move constructors are available, an rvalue argument invokes the move constructor, and likewise with copy and move assignment operators).