2015-01-21 2 views
3
#include <string> 
#include <iostream> 
#include <utility> 

struct A { 
    std::string s; 
    A() : s("test") {} 
    A(const A& o) : s(o.s) { std::cout << "move failed!\n"; } 
    A(A&& o) : s(std::move(o.s)) {} 
    A& operator=(const A&) { std::cout << "copy assigned\n"; return *this; } 
    A& operator=(A&& other) { 
     s = std::move(other.s); 
     std::cout << "move assigned\n";`enter code here` 
     return *this; 
    } 
}; 

A f(A a) { return a; } 

struct B : A { 
    std::string s2; 
    int n; 
    // implicit move assignment operator B& B::operator=(B&&) 
    // calls A's move assignment operator 
    // calls s2's move assignment operator 
    // and makes a bitwise copy of n 
}; 

struct C : B { 
    ~C() {}; // destructor prevents implicit move assignment 
}; 

struct D : B { 
    D() {} 
    ~D() {}; // destructor would prevent implicit move assignment 
    //D& operator=(D&&) = default; // force a move assignment anyway 
}; 

int main() 
{ 
    A a1, a2; 
    std::cout << "Trying to move-assign A from rvalue temporary\n"; 
    a1 = f(A()); // move-assignment from rvalue temporary 
    std::cout << "Trying to move-assign A from xvalue\n"; 
    a2 = std::move(a1); // move-assignment from xvalue 

    std::cout << "Trying to move-assign B\n"; 
    B b1, b2; 
    std::cout << "Before move, b1.s = \"" << b1.s << "\"\n"; 
    b2 = std::move(b1); // calls implicit move assignment 
    std::cout << "After move, b1.s = \"" << b1.s << "\"\n"; 

    std::cout << "Trying to move-assign C\n"; 
    C c1, c2; 
    c2 = std::move(c1); // calls the copy assignment operator 

    std::cout << "Trying to move-assign D\n"; 
    D d1, d2; 
// d2 = std::move(d1); 
} 

При выполнении a2 = std::move(a1) заявления, поведение отличается от выполнения оператора b2 = std::move(b1). В приведенном ниже заявлениине становится пустым после операции перемещения, а a1.s становится пустым после операции перемещения.Поведение операции станд :: двигаться в C++ 11

Может ли кто-нибудь сказать, что именно там происходит?

+0

Какой компилятор вы используете? – Praetorian

+0

@Praetorian Или, другими словами, почему, по вашему мнению, ваш компилятор совместим с C++ 11 ... – Yakk

+2

Что означает @Yakk, так это то, что это может быть артефактом ссылки gcc, посчитанной 'std :: string', которая является а не C++ 11. В любом случае нет гарантий относительно перемещенного из объекта, кроме того, что он находится в * действительном, но неуказанном состоянии *, поэтому библиотека может реализовать назначение переноса как вызов 'swap', который оставил бы источник, содержащий все назначения или он может реализовать его как вызов 'assign' (или что-то подобное), который оставил бы исходную строку пустой. – Praetorian

ответ

1

Обычно назначение переноса реализуется как своп на std::string, так почему строка должна быть пустой, поскольку она всегда инициализируется "test"?

Где вы видите, что a1.s становится пустым, так как нет его печати?

Я не вижу ничего странного поведения here. Оба обрабатываются одинаково.

+0

@ Jack Я наблюдал это при отладке кода. Значение a1.s изменяется после операции перемещения, но то же самое не происходит с b1.s, и оба они являются строками. Хотя a1 и b1 являются lvalues, я преобразовываю его в значение r с помощью std :: move() ... так почему это происходит. И, кроме того, в первом случае он не вызывает конструктор копирования, где, как и во втором случае, он вызывает конструктор копирования. Могу ли я узнать, как std :: move() работает в производном классе –

2

Одно из великих (и постоянных) заблуждений относительно ссылок на C++ 11 и rvalue заключается в том, что std::move что-то делает для объекта (или что-то в этом порядке).

Это не так. std::move действительно просто задает свой параметр для ссылочного типа rvalue и возвращает его. Все, что делается с объектом, происходит в конструкторе перемещения, перемещает оператор присваивания (и т. Д.) На основе того факта, что вызывается версия, которая принимает ссылку на rvalue (вместо того, чтобы принимать значение или значение lvalue).

Что касается конкретного вопроса, который вы задали, по крайней мере, исходя из комментариев в вашем коде, у вас, похоже, есть некоторые недоразумения. Комментарий к a2=std::move(a1); говорит, что вы выполняете «назначение перемещения из значения x». Это ... в лучшем случае вводит в заблуждение. Значение xvalue - это значение, которое будет немедленно отправлено в eXpire. Это довольно много для возвращаемого значения функции:

Foo &&bar() { 
    Foo f; 
    // ... 
    return f; 
} 

В этом случае bar() является xvalue потому bar возвращает ссылку RValue на объект, который истекает (выходит из области видимости), как функция завершает выполнение.

Что касается конкретного вопроса, который вы задали, я подозреваю, что в основном это сводится к вопросу о том, (и если да, именно так), ваша стандартная библиотека реализует конструктор перемещения для std::string. Например, при использовании g ++ (4.9.1) я получаю тот же результат: b1.s содержит test как до, так и после использования в качестве источника движения. С другой стороны, если я использую MS VC++ 14 CTP, я получаю b1.s="test" перед переходом и b1.s="" после переезда. Хотя я не тестировал его, я ожидал бы, что результаты с Clang будут одинаковыми. Короче говоря, похоже, что стандартная библиотека gcc на самом деле не реализует назначение/построение move для std::string (пока - по крайней мере, начиная с версии 4.9 - я еще не смотрел 5.0).

+3

'f' в' bar() 'не является значением xvalue; это lvalue. Существует специальное правило, в котором говорится, что в этой ситуации разрешение перегрузки «выполняется так, как если бы объект был обозначен rvalue», обратите внимание на условное условное выражение. Для 'A f (A a) {return a; } ', результат' f (A()) 'является значением prvalue. –

+0

Oops - thanks - предназначено для возврата ссылки rvalue, но почему-то не учитывает критический '&&' для возвращаемого типа ... –

+0

Выражение 'bar()' является значением xvalue, но 'f' в' bar () 'нет. –

Смежные вопросы