Почему T && создается как int &?

Может кто-нибудь объяснить, почему это компилируется и почему t заканчивается типом int&?

#include <utility>

void f(int& r)
{
    ++r;
}

template <typename Fun, typename T>
void g(Fun fun, T&& t) 
{ 
    fun(std::forward<T>(t)); 
}

int main()
{
    int i = 0;

    g(f, i);
}

Я вижу это на GCC 4.5.0 20100604 и GDB 7.2-60.2

Ответ 1

Из-за совершенной пересылки, когда аргумент P&& является lvalue, тогда P будет выводиться на тип аргумента плюс привязка &. Таким образом, вы получаете int & && с P int&. Если аргумент представляет собой rvalue, тогда P будет выводиться только для типа аргумента, поэтому вы получите аргумент int&& с P int, если вы передадите, например, 0 напрямую.

int& && будет сжиматься до int& (это семантический вид - синтаксически int& && является незаконным. Но говоря U &&, когда U является параметром шаблона или typedef, ссылающимся на тип int&, то U&& по-прежнему является типом int& - т.е. две ссылки "сворачиваются" на одну ссылку lvalue). Вот почему t имеет тип int&.

Ответ 2

Если по какой-то причине вы действительно хотите привязываться к значениям lvalues ​​или rvalues, используйте метапрограммирование:

#include <type_traits>

template <typename T>
typename std::enable_if<std::is_lvalue_reference<T&&>::value, void>::type
fun(T&& x)
{
    std::cout << "lvalue argument\n";
}

template <typename T>
typename std::enable_if<std::is_rvalue_reference<T&&>::value, void>::type
fun(T&& x)
{
    std::cout << "rvalue argument\n";
}

int main()
{
    int i = 42;
    fun(i);
    fun(42);
}