Нужно ли определять конструкторы перемещения из разных классов?

Рассмотрим следующее:

struct X
{
    Y y_;

    X(const Y & y) :y_(y) {}    
    X(Y && y) :y_(std::move(y)) {}
};

Нужно ли определять конструктор, как второй, чтобы в полной мере использовать семантику перемещения? Или он будет автоматически заботиться в соответствующих ситуациях?

Ответ 1

Да, но нет. Ваш код должен быть следующим:

struct X
{
    Y y_;

    X(Y y) : // either copy, move, or elide a Y
    y_(std::move(y)) // and move it to the member
    {} 
};

Если вы когда-либо говорите в дизайне "Мне нужна моя собственная копия этих данных" *, тогда вы должны просто взять аргумент по значению и перенести его туда, где он должен быть. Это не ваша задача, чтобы решить, как построить это значение, вплоть до доступных конструкторов для этого значения, поэтому пусть он делает этот выбор, что бы это ни было, и работает с конечным результатом.

* Это относится и к функциям, конечно, например:

void add_to_map(std::string x, int y) // either copy, move or elide a std::string
{
    // and move it to where it needs to be
    someMap.insert(std::make_pair(std::move(x), y));
}

Обратите внимание, что это тоже применяется в С++ 03, если тип по умолчанию конструктивен и сменяется (что все равно перемещает):

// C++03
struct X
{
    std::string y_;

    X(std::string y) // either copy or elide a std::string
    {
        swap(y_, y); // and "move" it to the member
    } 
};

Хотя это, похоже, не так широко сделано.

Ответ 2

Да, это необходимо. Константа ref может быть только копией, а не перемещением.