2016-02-09 7 views
1

Возможно, что-то я получил не полностью, но после прочтения «Использовать decltype для авто & & параметров для std :: forward them» (из Effective Modern C++) Я нашел что-то очень странно.Generic lambdas in C++ 14: странное поведение

Давайте предположим, что мы имеем

int size(std::string &&s) { std::cout <<"std::string&&" <<std::endl; return s.size(); } 
int size(const std::string &s) { std::cout <<"std::string const &" <<std::endl; return s.size(); } 

Я определил два lambas (один с автомобилем, и тот, который правильно вперед авто & & Param):

auto f = [](auto x) { return size(x); }; 

std::string s{ "buonanotte" }; 
std::cout <<f(s) <<std::endl; 
std::cout <<f("fiorellino") <<std::endl; 

auto g = [](auto&& x) { return size(std::forward<decltype(x)>(x)); }; 
std::cout <<g(s) <<std::endl; 
std::cout <<g("fiorellino") <<std::endl; 

Ну, результат тот же : f (s) и g (s) определяют размер (const std :: string &), тогда как f ("fiorellino") и g ("fiorellino") вызывают перегрузку размера для rvalues.

Мой вопрос: почему это происходит? Должна ли не только вторая лямбда различать lvalue и rvalue? Я ожидал, что первая лямбда (f()) вызывает двойной размер (const std :: string &), но, видимо, этого не происходит.

Я что-то не так?

+0

Не могли бы вы немного рассказать об этом? Я взял этот пример непосредственно из книги Скотта Мейерса. – Stadium

+0

Ну, а не 'f (" fiorellino ")' приводит к созданию временной строки 'std :: string' (которая никогда не привязана к lvalue)? Поэтому я не вижу проблемы с выбранной перегрузкой rvalue. _ «Когда используются в качестве аргумента функции, и когда две перегрузок функций доступны, один захват Rvalue ссылочного параметра, а другая принимают именующая ссылка на сопзЬ параметра, Rvalue связываются с эталонной Rvalue перегрузки» _. – Michael

+1

@ nwp: Абсолютно неправильно. – ildjarn

ответ

3

Должна ли не только вторая лямбда различать lvalue и rvalue? Я ожидал, что первую лямбду (F()) вызов дважды размера (константные зОго :: строки &)

Строкового литерала "fiorellino" является именующим, но при прохождении его size, временный std::string объект будет построен из этого строкового литерала, а это временное значение r.

То есть тип x в f для f("fiorellino") будет const char*. То, что const char* используется для создания временного std:string (rvalue), когда f звонит size.

Это, в свою очередь, приводит к перегрузке RValue быть выбран для f("fiorellino") на основе этого: «При использовании в качестве аргумента функции, и когда две перегрузок функций доступны, один принимают опорный Rvalue параметр, а другая принимая именующую ссылку Const параметр, Rvalue связывается с опорным Rvalue перегрузки» (source).

+0

'x', однако, является значением lvalue, но иногда оно рассматривается как rvalue. – nwp

+0

Хорошо, теперь я понял. Я сделал очень маленький тест, добавив одну функцию, которая (надеюсь) на этот раз генерирует реальное значение rvalue: я добавил std :: string generate (int x) {return std :: to_string (x); } и теперь при вызове f (generate (123)) вызывается lvalue-перегрузка, а при использовании g (generate (123)) вызывается правильный ссылочный номер rvalue. Большое спасибо! – Stadium

+0

@Stadium Можете ли вы написать ответ? Я еще не понял. – nwp

0

OK,

, так что я хотел бы опубликовать полный ответ (Майкл, опять-таки, спасибо). Дела я путаюсь в том, что «fiorellino» является Rvalue и временным создан для него, и этого временного объекта, когда константный T & и T & & доступны всегда будет связываться с версией T & &. Все идет нормально. Это имеет смысл (см. http://en.cppreference.com/w/cpp/language/value_category, где описаны значения r).

Теперь я изменил мой пример таким образом:

int size(std::string &&s) { std::cout <<"std::string&&" <<std::endl; return s.size(); } 
int size(const std::string &s) { std::cout <<"std::string const &" <<std::endl; return s.size(); } 

std::string generate(int x) { return std::to_string(x); } 

auto f = [](auto x) { return size(x); }; 

std::string s{ "buonanotte" }; 
std::cout <<"f(): "; f(s); 
std::cout <<"f(): "; f("fiorellino"); 
std::cout <<"f(): "; f(generate(123)); 
std::cout <<"f(): "; f(std::move(s)); 

auto g = [](auto&& x) { return size(std::forward<decltype(x)>(x)); }; 

std::string s2{ "buonanotte" }; 
std::cout <<"g(): "; g(s2); 
std::cout <<"g(): "; g("fiorellino"); 
std::cout <<"g(): "; g(generate(123)); 
std::cout <<"g(): "; g(std::move(s2)); 

Теперь генерирует (123) создает правильное временное, что будет неправильно перенаправлены первой лямбдой, но правильно перенаправлять второй.

Посмотрите на выходе:

f(): std::string const & 
f(): std::string&& 
f(): std::string const & 
f(): std::string const & 
g(): std::string const & 
g(): std::string&& 
g(): std::string&& 
g(): std::string&& 

, даже если я использовал зЬй :: ход (ы) (что создает RValue-ссылку) первый лямбда всегда выбирает & перегрузку Const T.

+2

_ "fiorellino" является rvalue "_ Обратите внимание, что строковый литерал' 'fiorellino'' является _lvalue_; временный объект 'std :: string', построенный из него, является rvalue. – Michael

Смежные вопросы