Должен ли я переместить членов std :: exchange ed? - PullRequest
2 голосов
/ 17 октября 2019

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);

Следовательно,

  • следует явно использовать std::move дляих, так что я могу убедиться, что члены были на 100% перемещены в объект other?
  • Если Да , будет ли более громоздким использование std::exchange для этогосценарий?

Я использую Visual Studio 2017 с флагом компилятора C ++ 14.

1 Ответ

3 голосов
/ 17 октября 2019

Нет, использование 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 изменяет правила копирования, но это что-то другое

С здесь вы можете видеть, что prvalue это:

вызов функции или перегруженное выражение оператора,тип возвращаемого значения не является ссылочным, например str.substr (1, 2), str1 + str2 или it ++

И свойства prvalue (как подмножество rvalues) имеют следующие значения (выделено мое):

Значение r может использоваться для инициализации ссылки rvalue , и в этом случае время жизни объекта, идентифицированного значением r, увеличивается до тех пор, пока не закончится область ссылки.

При использовании в качестве аргумента функции и при наличии двух перегрузок функции одна принимает параметр ссылки rvalue, а другая - ссылку lvalue на параметр const, rvalue связывается с перегружением ссылки rvalue (таким образом, если доступны конструкторы копирования и перемещения, аргумент rvalue вызывает конструктор перемещения, а также с помощью операторов присваивания копирования и перемещения).

...