Перемещение конструкторов и ссылок на значения - PullRequest
0 голосов
/ 10 октября 2018
class Foo {
    int m_num;
public:
    Foo() {}

    Foo(int& n) {m_num = n;}

    Foo(int&& n) {m_num = n;}

    Foo(const Foo& src) {m_num = src.m_num;}

    Foo(Foo&& src) {m_num = src.m_num;}

    Foo& operator =(const Foo& src) {
        m_num = src.m_num;
        return *this;
    }

    Foo& operator =(Foo&& src) {// move
        m_num = src.m_num;
        return *this;
    }

    int& operator =(const int& src) {return m_num = src;}

    int& operator =(int&& src) {return m_num = src;}
};

Почему при вызове Foo f4(Foo(2)); он вызывает конструктор Foo(int&& n) вместо Foo(Foo&& src)?Кроме того, почему передача num, являющаяся ссылочным вызовом rvalue, не перемещает конструкторов?Например, Foo f = num не вызывает конструктор перемещения.

int main() {
    int&& num = 5;
    /*int num{ 5 };*/ // initializer-list
    Foo f = num; // custom constructor
    f = num; // assignment operator
    Foo f2 = 6; // custom move constructor
    f2 = 10; // custom move assignment
    f2 = f; // assignment operator
    Foo f3(f); // custom constructor
    // f3(f2); // ERROR
    f3 = (f2); // assignment operator

    Foo f4(Foo(2));

1 Ответ

0 голосов
/ 10 октября 2018

В Foo(2) целое число является rvalue, и необходим ссылочный конструктор rvalue из int&&.

В случае

 Foo f = num; // custom constructor 

Конструктор rvalue не вызывается,потому что именованные значения никогда не рассматриваются как rvalue.Вы должны вызвать std::move(num), чтобы заставить его работать.

Стандарт определяет это таким образом, чтобы избежать путаницы в случаях, когда именованные значения перемещаются неожиданно и используются позже.Вы должны четко указывать перемещение, даже если переменная является ссылкой на rvalue.

edit

Согласно cppreference компилятор c ++ 17 должен исключить копию /Конструктор перемещения (т.е. он не должен вызывать или требовать наличия конструктора копирования / перемещения в этом случае, и поэтому вы видите только конструктор из int&&):

Foo f4(Foo(2));

Вот цитатаиз cppreference (который не так хорош, как стандарт, но достаточно близок):

языковые правила гарантируют, что операция копирования / перемещения не выполняется, даже концептуально:

  • При инициализации переменной, когда выражение инициализатора является prvalue того же типа класса (игнорируя квалификацию cv), что и тип переменной:

    T x = T (T (T ())));// только один вызов конструктора по умолчанию для T, чтобы инициализировать x

...