2015-08-31 3 views
2

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

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. Эта функция вызывает оператор = move.

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); имеют такое же поведение?

+8

'DefaultConstructor testConstructor2 = std :: move (testConstructor);' is initialisation, not assign. –

+0

Заявить 'testConstructor2' отдельно заранее, например. 'DefaultConstructor testConstructor2 ({});' –

+0

Джоната Поттер выглядит следующим образом. Спасибо. – PeterNL

ответ

8

Синтаксис

DefaultConstructor testConstructor2 = something; 

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

2

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

6

Это:

T foo = bar; 

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

T foo(bar); 

Разница заключается в том, что последний вызов прямой функции конструктору T «s, в то время как прежние попытки построить неявную последовательность преобразования из decltype(bar) в T. Таким образом, случаи, когда прямая инициализация завершается успешно, но инициализация копирования может завершиться неудачей. В любом случае инициализация инициализируется: это вызов конструктора, а не вызов назначения.

В нашем случае, хотя эти две линии точно эквивалентны:

DefaultConstructor testConstructor2 = std::move(testConstructor); 
DefaultConstructor testConstructor2{std::move(testConstructor)}; 

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

+1

Просто из любопытства, когда 'T foo = bar;' не будет эквивалентно 'T foo (bar)'? –

+3

@PCLuddite Когда вам нужно несколько преобразований. например: 'struct A {A (int) {}}; struct B {B (A) {}}; 'Учитывая это,' B b (4); 'преуспевает, но' B b = 4; 'не будет. – Barry

+2

@PCLuddite Также, когда у вас есть конструктор 'explicit ', который лучше подходит (или единственное возможное совпадение, которое делает ошибку' = 'case). 'struct A {Явный A (bool) {/ * # 1 * /} A (int) {/ * # 2 * /}}; A a = true;/* вызывает # 2 */A b (false);/* вызывает # 1 */', и когда рассматриваемый класс не может быть скопирован/перемещен:' struct B {B (B &&) = delete; B (int) {}}; B b (1);/* OK */B b2 = 1;/* error */' –