私は可変個引数テンプレートと引数転送を実験してきました。一貫性のない動作を見つけたと思います。
説明のために、このプログラムは次のとおりです。
#include <iostream>
#include <typeinfo>
#include <tuple>
#include <cxxabi.h>
template <typename... Args> struct X {};
struct A {
A () {}
A (const A &) {
std :: cout << "copy\n";
}
};
template <typename T> const char * type_name () {
return abi :: __cxa_demangle (typeid (T) .name (), 0, 0, NULL);
}
void foo () {}
template <typename... Args>
void foo (const A &, Args ... args) {
std :: cout << type_name <X <Args...>> () << "\n“; foo (args...);
}
int main () {
foo (A(), A());
}
以下を出力します。
X<A, A>
copy
X<A>
fooのテンプレート特殊化にはconst参照の最初の引数がありますが、X<A>
出力が示すように、テンプレート型は非参照型として推定されるため、可変引数は値によって渡されます。
だから、私はトーマスベッカーに相談します:
template<typename T> void foo(T&&);
ここでは、以下が適用されます。
タイプAの左辺値でfooが呼び出されると、TはA&に解決されるため、上記の参照折りたたみルールにより、引数タイプは事実上A&になります。
タイプAの右辺値でfooが呼び出されると、TはAに解決されるため、引数タイプはA&&になります。
そしてこれを試してください:
template <typename... Args>
void foo (const A &, Args && ... args) {
std :: cout << type_name<X<Args...>>() << "\n";
foo (args...);
}
どの出力:
X<A, A>
X<A&>
今、私は困惑しています。ここでは、fooの呼び出しが3つあります。私の頭の中では、 (A()は名前のない右辺値であるため、参照であるため)、過負荷で解決するものをmain()
推測する必要があります。これは順番に推論します。foo<A,A,A>(A&&,A&&,A&&)
foo<A,A,A>(const A&,A&&,A&&)
foo<A,A>(A&&,A&&)
質問は:どうしてX<A,A>
非参照A
がX<A&>
ありますが、参照があるのA
ですか?
std::forward
再帰で使用できないため、これにより問題が発生します。