Изтрит конструктор за копиране на член на кортеж причинява грешка

Тази част от кода

#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_;
};

N.B. std::move тук е напълно безполезно:

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

Всичко, което std::move прави, е да преобразува своя аргумент към rvalue, но временният Foo(1) вече е rvalue, така че вие ​​прехвърляте rvalue към rvalue: безполезно. Освен това, без std::move компилаторът може да копира elision и да оптимизира действителното преместване, но когато използвайте 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