Почему поведение оператора перемещения C++11 ( = ) отличается


Я протестировал семантику перемещения в C++11. Я написал класс с конструктором move.

class DefaultConstructor
{
public:
    DefaultConstructor(std::vector<int> test) :
        m_vec(std::forward<std::vector<int>>(test))
    {

    };

    DefaultConstructor(DefaultConstructor &&def) :
        m_vec(std::forward<std::vector<int>>(def.m_vec))
    {
    }

    DefaultConstructor& operator=(DefaultConstructor&& def) {
        m_vec = std::move(def.m_vec);
        return *this;
    }

    DefaultConstructor& operator=(const DefaultConstructor&) = delete;
    DefaultConstructor(DefaultConstructor &) = delete;

    std::vector<int> m_vec;
};

Я написал основную функцию, которая использует семантику перемещения. Я понимаю, что происходит в движении семантическом и это отличный инструмент. Но есть такое поведение, которое для меня не объяснимо. Когда я вызываю основную функцию DefaultConstructor testConstructor2 = std::move(testConstructor); , для меня должна вызываться DefaultConstructor& operator=(DefaultConstructor&& def). Но Visual Studio 2015 вызывает конструктор перемещения.

int main()
{
    std::vector<int> test = { 1, 2, 3, 4, 5 };
    DefaultConstructor testConstructor(std::move(test));

    DefaultConstructor testConstructor2 = std::move(testConstructor);
    DefaultConstructor &testConstructor3 = DefaultConstructor({ 6, 7, 8, 9 });
    DefaultConstructor testConstructor4 = std::move(testConstructor3);
    swapMove(testConstructor, testConstructor2);
}

Хорошо, я подумал, что, возможно, оператор = Move не нужен больше не. Но я попробовал использовать функцию SwapMove. Функция вызова оператора = двигаться.

template<typename T>
void swapMove(T &a, T &b)
{
    T tmp(std::move(a));
    a = std::move(b);
    b = std::move(tmp);
}
Может ли кто-нибудь объяснить, в чем именно заключается разница между этими двумя вызовами? Разве вызовы a = std::move(b); и DefaultConstructor testConstructor2 = std::move(testConstructor); не должны иметь одинаковое поведение?
3 2

3 ответа:

Синтаксис

 DefaultConstructor testConstructor2 = something;

Всегда вызывает конструктор, потому что объект testConstructor2 еще не существует. оператор= может быть вызван только в контексте уже построенного объекта.

Это:

T foo = bar;

Называется копирование-инициализация. Это обычно, но не всегда, эквивалентно:

T foo(bar);
Отличие состоит в том, что последний является прямым вызовом функции конструктора T, в то время как первый пытается построить неявную последовательность преобразования из decltype(bar) в T. Таким образом, существуют случаи, когда прямая инициализация успешна, но инициализация копирования может потерпеть неудачу. В любом случае, инициализация-это инициализация: это вызов конструктора, а не вызов на задание. Однако в нашем случае эти две строки в точности эквивалентны:
DefaultConstructor testConstructor2 = std::move(testConstructor);
DefaultConstructor testConstructor2{std::move(testConstructor)};

И ни один из них не звонит DefaultConstructor::operator=.

DefaultConstructor testConstructor2 = std::move(testConstructor); это строительство, а не назначение. Это в точности аналогично копированию конструкции против назначения в том же самом виде кода до C++11.