我一直在试验可变参数模板和参数转发。我想我发现了一些不一致的行为。
为了说明,这个程序:
#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>
输出所示。
所以,我咨询了Thomas Becker:
template<typename T> void foo(T&&);
在这里,以下适用:
当 foo 在 A 类型的左值上调用时,T 会解析为 A&,因此,根据上面的引用折叠规则,参数类型实际上变成了 A&。
当 foo 在类型 A 的右值上调用时,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 的三个调用。在我的脑海中,main()
应该推断foo<A,A,A>(A&&,A&&,A&&)
(因为 A() 是一个未命名的右值,因此是一个引用),它重载解析为foo<A,A,A>(const A&,A&&,A&&)
. 这反过来又推导foo<A,A>(A&&,A&&)
出来等等。
问题是:为什么X<A,A>
有 non-reference A
s 但X<A&>
有 reference A
?
这会导致问题,因为我不能std::forward
在递归中使用。