2015-10-21 4 views
3

я видел использование что-то вродеПочему шаблонная ссылка rvalue принимает значения lvalues?

#include <iostream> 
#include <functional> 
using namespace std; 

template<typename FN> 
void Foo(FN&& Fn) 
{ 
    Fn(); 
} 


void b() 
{ 
    cout << "2." << endl; 
} 

int main() 
{ 
    Foo([](){ cout << "1." << endl; }); 
    Foo(&b); 

    auto c = []() { cout << "3." << endl; }; 

    Foo(c); 

    std::function<void(void)> d = c; 

    Foo(d); 

    return 0; 
} 

Я довольно уверен «с» именующий, но я считаю, что есть некоторые дедукции махинация типа лямбды. Но я почти на 100% уверен, d является lvalue.

Почему шаблонный материал работает, если функция принимает значение r, но d является значением lvalue?

Кроме того, почему бы один написать подпись Foo как, что вместо того, чтобы просто

template<typename FN> 
void Foo(FN Fn) 
+2

Это не действительно rvalue: это называется «универсальная ссылка» (или «ссылка на перемотку», но первая выражает идею лучше) из-за вычитания типа шаблона и списания ссылок. – edmz

+0

@black Правильный термин - «пересылка ссылки» (потому что это, вероятно, должно быть 'std :: forward()' -ed).«Универсальная ссылка» - это то, что использует Скотт Майерс. – Barry

ответ

2

правила для удержания T&& хитры.

Они предназначены для того, чтобы сделать вывод T&& «ссылкой для пересылки» (или «универсальной ссылкой»).

Во-первых, ссылка рушится. Предположим, у вас неизвестный тип X. На данный момент X не выводится.

Тогда, если мы рассмотрим переменные следующего типа:

typedef X x0; 
typedef X& x1; 
typedef X const& x2; 
typedef X&& x3; 

и мы X быть один из int, int&, int const& и int&&, мы получаем:

X is ---> int   int&  int const&  int&& 
X   int   int&  int const&  int&& 
X&   int&  int&  int const&  int& 
X const& int const& int&  int const&  int& 
X&&  int&&  int&  int const&  int&& 

live example.

Следующий бит содержит правила вычета. Если вы пройдете X& до T&& в выведенном контексте, то T выведено как X&. Это приводит к тому, что T&& становится X& по приведенным выше правилам свертывания. Аналогичные вещи случаются для X const&.

Если вы пройдете X&& до T&&, оно выводит T в X. T&& будет X&& как есть.

Между двумя из них, в выведенном контексте, template<class T> void foo(T&&t) является универсальной ссылкой (ну, теперь это называется ссылкой пересылки).

Вы можете восстановить категорию значений r/l t с помощью std::forward<T>(t), отсюда ссылка на пересылку имени.

Это позволяет одному шаблону обрабатывать как значения l, так и r, и использовать std::forward и аналогичные машины, чтобы вести себя по-другому, если хотите.

Только обработка rvalues ​​требует дополнительной работы: вы должны использовать SFINAE или другую перегрузку (возможно, с =delete). Только обрабатывать lvalues ​​легко (просто выведите T&).

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