この例では、右辺値参照と転送参照の違いが、ScottMeyersによって十分に明確にされています。
Widget&& var1 = someWidget; // here, “&&” means rvalue reference (1)
auto&& var2 = var1; // here, “&&” does not mean rvalue reference (2)
template<typename T>
void f(std::vector<T>&& param); // here, “&&” means rvalue reference (3)
template<typename T>
void f(T&& param); // here, “&&”does not mean rvalue reference (4)
基本的に、区別は控除可能なコンテキストがある場合に発生します。したがって、ケース(3)は、vector<...>&&
があることを明示的に示していますが、ケース(4)のT
は推定され、(参照折りたたみルールを適用した後)「値カテゴリ」の観点から分類されます。
しかし、もう少し複雑なパターンマッチングではどうなりますか?次の場合を例にとってみましょう:
template <template <class...> class Tuple, class... Ts>
void f(Tuple<Ts...>&& arg)
{
}
ここで&&
はどういう意味ですか?
最後の例では、arg
は右辺値の参照です。
転送参照はcv非修飾テンプレートパラメーターへの右辺値参照です
およびTuple<Ts...>
はテンプレートパラメータではありません。
([temp.deduct.call]からの引用。)
これは右辺値参照であり、転送参照ではありません。
確実にする最も簡単な方法は、左辺値を渡そうとすることです。失敗した場合は右辺値参照、そうでない場合は転送参照です。
template<typename... Ts>
struct foo {};
//f function definition
int main() {
foo<int, double> bar;
f(bar); // fails! Cannot bind lvalue to rvalue reference
f(foo<int, double>{}); // ok, rvalue is passed
}
参照の転送という概念は標準的な概念ではありません。参照を確認すると便利ですが、正しく理解して処理する場合は、参照演算を理解する必要があります。 (マイヤーの本にもそれについての章があると思います)
転送参照の概念の背後にあるのは、参照演算です。
転送参照を使用してコンパイラテンプレート型の推定をシミュレートしてみましょう
_template<class T>
void foo(T&&);
//...
const int i=42;
foo(i); // the compiler will defines T = const int &
// T&& = const int & && = const int &
// => the compiler instantiates void foo<const int &>(const int &);
foo(6*7);// the compiler will defines T = int
// T&& = int &&
// the compiler instantiates void foo<int>(int &&);
_
このような状況では、テンプレートfooのインスタンス化により、左辺値参照によって引数を取る関数、または引数右値参照を取る関数が生成されます。転送参照は、テンプレートタイプの推定に応じて、右辺値参照または左辺値参照のいずれかになります。このような状況では、パラメーターは左辺値またはx値のいずれかとして渡されるため、このように名前が付けられます。これはT&& std::forward<T>(T&& a)
の仕事です。
関数を宣言すると、次のようになります。
_ template<class T>
void foo(ATemplateClass<T> && a);
_
コンパイラーによってTに対して推定されたタイプが何であれ、右辺値参照パラメーターを取得します。