template で T&&

rvalue reference については、こことか、こことか、ここら辺に詳しく書いてあります。


さて、rvalue reference ですが、簡単に説明すると次の様な挙動になります。

int n = 0;

int&& x1 = 0;
int&& x2 = n;   // error

これは関数で受け取る場合も同様で、

void
func(int&&){}

func(0);
func(n);    // error

このように lvalue で受け取る事は出来ません。
ただし、template 引数で受け取る場合はこれに限らず、

template<typename T>
void
T_func(T&&){}

T_func(0);
T_func(n);      // ok     T が int& となり && は無視される
T_func<int>(n); // error  int&& で受け取ろうとする

このように、lvalue でも受け取ることができます。
これは、T が T& になり、&& が無視されるからです。
(結果的に lvalue reference で受け取る)
これで T&& と書いておけば rvalue でも lvaleu でも問題なく受け取る事が出来ます。
ただし、明示的に型を指定する場合はその限りではないので注意して下さい。

[注意]

次のコードはエラーになります。

template<typename T>
struct holder{
	T value;
};

template<typename T>
holder<T>
make_holder(T&& t){
	return { t };
}

// error: conversion from 'holder<int&>' to non-scalar type 'holder<int>' requested
int n = 0;
holder<int> v = make_holder(n);

これは、T が T& になり、holder を返している為です。
これを解決するためには、std::remove_reference を使用します。

#include <type_traits>

template<typename T>
struct holder{
    T value;
};

template<typename T>
holder<typename std::remove_reference<T>::type>
make_holder(T&& t){
   return { std::forward<T>(t) };
}

int n = 0;
holder<int> v = make_holder(n); // ok