Удаленный конструктор копирования члена кортежа вызывает ошибку

Этот фрагмент кода

#include <tuple>

struct Foo
{
    Foo(const int& value):value_(value){}

    //Foo(const Foo&)=delete; // delete copy constructor

    int value_;
};

int main()
{
    std::tuple<Foo> tup(std::move(Foo(1)));
    return 0;
}

работает нормально, но если вы удалите конструктор копирования Foo, произойдет сбой со следующей ошибкой компиляции: использование удаленной функции Foo::Foo(const Foo&).

Но, поскольку я прямо указываю, что объект можно перемещать, почему конструктор std::tuple использует конструктор копирования Foo вместо конструктора перемещения? Как я могу заставить std::tuple быть построенным, перемещая экземпляр Foo вместо его копирования?


person Marco Agnese    schedule 28.09.2015    source источник
comment
Объявление конструктора-копии (как удаленного или нет) запрещает генерацию конструктора-перемещения. Затем только конструктор копирования может принять Foo&&, и он удаляется.   -  person Quentin    schedule 28.09.2015
comment
Поэтому я могу просто исправить это, добавив следующий движущийся конструктор Foo(const Foo&& foo){value_=foo.value_;}. Я прав? Таким образом, Foo можно перемещать, а не копировать.   -  person Marco Agnese    schedule 28.09.2015
comment
@MarcoAgnese, нет, это неправильно. Конструкторы перемещения, которые принимают const аргументов, бесполезны. Смотрите мой ответ.   -  person Jonathan Wakely    schedule 28.09.2015
comment
Вам не нужно вызывать std::move с аргументом, который уже является rvalue   -  person Piotr Skotnicki    schedule 28.09.2015
comment
@JonathanWakely Я скопировал и вставил и забыл удалить квалификатор const :)   -  person Marco Agnese    schedule 28.09.2015


Ответы (1)


Но, поскольку я прямо говорю, что объект можно перемещать,

Нет, вы сообщаете компилятору, что хотите переместить объект. Это не то же самое.

почему конструктор std::tuple использует конструктор копирования Foo вместо своего движущегося конструктора?

Потому что, когда вы удаляете конструктор копирования, неявный конструктор перемещения не существует, поэтому его нельзя использовать.

Как я могу заставить std::tuple быть сконструированным, перемещая экземпляр Foo вместо его копирования?

Либо не удаляйте конструктор копирования, либо также определяйте конструктор перемещения:

struct Foo
{
    Foo(const int& value):value_(value){}

    Foo(const Foo&)=delete; // delete copy constructor

    Foo(Foo&&)=default;

    int value_;
};

Н.Б. std::move здесь совершенно бесполезно:

std::tuple<Foo> tup(std::move(Foo(1)));

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

Оптимальный код — самый простой вариант:

std::tuple<Foo> tup(Foo(1));

Или еще проще:

std::tuple<Foo> tup(1);
person Jonathan Wakely    schedule 28.09.2015
comment
почему избыточное литье препятствует удалению копии? - person Marco Agnese; 28.09.2015
comment
Поскольку вы больше не просто конструируете tup из Foo(1), вы конструируете tup из результата некоторой функции, которая передается Foo(1) в качестве аргумента. Правила исключения копирования разрешают это только в определенных случаях, и это не один из них. - person Jonathan Wakely; 28.09.2015