Вопрос про конструктор перемещения
От: PM  
Дата: 23.02.15 10:01
Оценка:
Всем привет,

Обнаружил, что не понимаю, почему вызывается шаблонный конструктор перемещения, вместо нешаблонного в таком коде:
struct Y
{
    Y() { std::cout << "default ctor\n"; }

    Y(Y&&) { std::cout << "move ctor\n"; }
    template<typename T>
    Y(T&& y) { std::cout << "move<T> ctor\n"; }
};

int main()
{
    Y y1;
    Y y2 = y1;  // ожидаю move ctor, получаю move<T> ctor
    Y y3 = 22;  // здесь как и должно быть, move<T> ctor
}


Это как-то неожиданно, так как конструкторы копирования работают ожидаемо:
struct Y
{
    Y() { std::cout << "default ctor\n"; }

    Y(Y const&) { std::cout << "copy ctor\n"; }
    template<typename T>
    Y(T const& y) { std::cout << "copy<T> ctor\n"; }
};

int main()
{
    Y y1;
    Y y2 = y1;  // copy ctor
    Y y3 = 22;  // copy<T> ctor
}


Беглый просмотр разделов "Copy constructors", "Move constructors" и "Memeber templates" на cppreference.com ясности не добавил. В итоге проблему обошел с помощью enable_if, но непонимание причины осталось. Есть только предположение, что тут какие-то тонкости copy elision.

Решение с enable_if:
struct Y
{
    Y() { std::cout << "default ctor\n"; }

    Y(const Y&) { std::cout << "copy ctor\n"; }
    template<typename T>
    Y(T const&) { std::cout << "copy<T> ctor\n"; }

    Y(Y&&) { std::cout << "move ctor\n"; }
    template<typename T,
        typename = typename std::enable_if<!std::is_same<Y, typename std::decay<T>::type>::value>::type>
    Y(T&& y) { std::cout << "move<T> ctor\n"; }
};

int main()
{
    Y y1;
    Y y2 = y1;              // copy ctor
    Y y3 = std::move(y1);   // move ctor
    Y y4 = true;            // move<T> ctor
}


Код: http://coliru.stacked-crooked.com/a/57c4420a80d33963
Re: Вопрос про конструктор перемещения
От: niXman Ниоткуда https://github.com/niXman
Дата: 23.02.15 10:05
Оценка: +2
тут Y(T&& y), насколько я понимаю, y является универсальной ссылкой, а не r-value ссылкой.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[2]: Вопрос про конструктор перемещения
От: PM  
Дата: 23.02.15 10:14
Оценка:
Здравствуйте, niXman, Вы писали:

X>тут Y(T&& y), насколько я понимаю, y является универсальной ссылкой, а не r-value ссылкой.


Допустим, но ведь по правилам перегрузки Y(Y&&) должно быть более подходящей кандидатурой, чем Y(T&& y), или я что-то упускаю?
Re[3]: Вопрос про конструктор перемещения
От: niXman Ниоткуда https://github.com/niXman
Дата: 23.02.15 10:21
Оценка: 13 (2) +1
Y y2 = y1; // ожидаю move ctor, получаю move<T> ctor
тут не может быть вызван move-ctor, ибо y1 является l-value.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[4]: Вопрос про конструктор перемещения
От: niXman Ниоткуда https://github.com/niXman
Дата: 23.02.15 10:23
Оценка:
вообще, очень сложно отвечать, потому как код разбросан по трем сниппетам. невозможно на них ссылаться.
лучше, приведи один пример, с одним конкретным вопросом.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[3]: Вопрос про конструктор перемещения
От: jazzer Россия Skype: enerjazzer
Дата: 23.02.15 10:24
Оценка: 10 (1)
Здравствуйте, PM, Вы писали:

PM>Здравствуйте, niXman, Вы писали:


X>>тут Y(T&& y), насколько я понимаю, y является универсальной ссылкой, а не r-value ссылкой.


PM>Допустим, но ведь по правилам перегрузки Y(Y&&) должно быть более подходящей кандидатурой, чем Y(T&& y), или я что-то упускаю?


Упускаешь правила работы универсальных ссылок.
Коротко: если ты ей даешь на вход rvalue — она даст тебе rvalue reference. Если lvalue — даст lvalue reference.
Соответственно в вызове Y y2 = y1; универсальная ссылка превращается в обычную ссылку Y(Y&), что является более подходящим, чем Y(Y&&).
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[4]: Вопрос про конструктор перемещения
От: PM  
Дата: 23.02.15 10:24
Оценка:
Здравствуйте, niXman, Вы писали:

X>Y y2 = y1; // ожидаю move ctor, получаю move<T> ctor

X>тут не может быть вызван move-ctor, ибо y1 является l-value.

Спасибо, пазл сложился
Re[4]: Вопрос про конструктор перемещения
От: PM  
Дата: 23.02.15 10:34
Оценка:
Здравствуйте, jazzer, Вы писали:

X>>>тут Y(T&& y), насколько я понимаю, y является универсальной ссылкой, а не r-value ссылкой.


PM>>Допустим, но ведь по правилам перегрузки Y(Y&&) должно быть более подходящей кандидатурой, чем Y(T&& y), или я что-то упускаю?


J>Упускаешь правила работы универсальных ссылок.

J>Коротко: если ты ей даешь на вход rvalue — она даст тебе rvalue reference. Если lvalue — даст lvalue reference.
J>Соответственно в вызове Y y2 = y1; универсальная ссылка превращается в обычную ссылку Y(Y&), что является более подходящим, чем Y(Y&&).

Да, точно, нужен ведь явный move чтобы получить rvalue reference из именованного объекта. Спасибо, стало понятнее.

Какое новое поле граблей добавилось в C++11 с появлением новых типов ссылок, особенно если еще вспомнить про правила default/delete для конструкторов и операторов присваивания
Re[5]: Вопрос про конструктор перемещения [offtop]
От: SaZ  
Дата: 26.02.15 08:47
Оценка:
Здравствуйте, PM, Вы писали:

PM>Какое новое поле граблей добавилось в C++11 с появлением новых типов ссылок, особенно если еще вспомнить про правила default/delete для конструкторов и операторов присваивания


Ничего, зато С++ программисты по прежнему будут много зарабатывать. Ибо опять увеличился порог вхождения.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.