以可变 class 模板作为函数调用参数的函数模板参数推导
Function template argument deduction with variadic class template as function call parameter
具体来说,
template<class...> struct Tuple { };
template< class... Types> void g(Tuple<Types ...>); // #1
// template<class T1, class... Types> void g(Tuple<T1, Types ...>); // #2
template<class T1, class... Types> void g(Tuple<T1, Types& ...>); // #3
g(Tuple<>()); // calls #1
g(Tuple<int, float>()); // calls #2
g(Tuple<int, float&>()); // calls #3
g(Tuple<int>()); // calls #3
在 #2
取消注释的情况下,g() 将按照注释中的描述进行解析。令我惊讶的是,如果我注释掉 #2
行,g() 的调用将按如下方式解析:
g(Tuple<>()); // calls #1
g(Tuple<int, float>()); // calls **#1 ???? why not #3????**
g(Tuple<int, float&>()); // calls #3
g(Tuple<int>()); // calls #3
从下面的示例和解释中我看不出为什么 g(Tuple<int, float>());
不能解析为 #3
。是以下两条规则的方向应用:
If a parameter pack appears as the last P, then the type P is matched against the type A of each remaining argument of the call. Each match deduces the template arguments for the next position in the pack expansion.
template<class ... Types> void f(Types& ...);
void h(int x, float& y) {
const int z = x;
f(x, y, z); // P=Types&..., A1=x: deduces the first member of Types... to int
// P=Types&..., A2=y: deduces the second member of Types... to float
// P=Types&..., A3=z: deduces the third member of Types... to const int
// calls f<int, float, const int>
If P has one of the forms that include a template parameter list <T>
or <I>
, then each element Pi of that template argument list is matched against the corresponding template argument Ai of its A. If the last Pi is a pack expansion, then its pattern is compared against each remaining argument in the template argument list of A. A trailing parameter pack that is not otherwise deduced, is deduced to an empty parameter pack.
你的两个例子之间存在误解。在带有 f
的第二个示例中,您正在推导出对函数的引用 arguments。在带有 g
的第一个示例中,您正在推导出对函数参数的引用 模板参数 。后者必须完全匹配,但前者是根据引用的类型推导出来的。他们不一样。
在你的第一个例子中,
g(Tuple<int, float>());
无法调用 g(Tuple<T1, Types&...>)
。模板推导过程是关于选择一个推导的参数类型,该类型与被调用的参数类型相同。有一些例外(对于引用的 cv 限定、指针、派生 类、数组、函数),但其中 none 适用于此处。我们只需要选择 T1
和 Types...
,这样 Tuple<T1, Types&...>
与 Tuple<int, float>
的类型相同。这是不可能的,因为没有 Types&...
是 {float}
的包 Types...
,因为 float
不是参考!
所以一旦你注释掉 (2),就只有一个可行的候选者:(1)。
另一方面,
template<class ... Types> void f(Types& ...);
void h(int x, float& y) {
const int z = x;
f(x, y, z);
}
这里,Types&...
实际上是参数本身的类型(而不是它的模板参数),所以(temp.deduct.call):
If P is a reference type, the type referred to by P is used for type deduction.
我们推断 Types...
以匹配参数。这是成功的,因为所有参数都是左值,我们只是选择 {int, float, const int}
。
具体来说,
template<class...> struct Tuple { };
template< class... Types> void g(Tuple<Types ...>); // #1
// template<class T1, class... Types> void g(Tuple<T1, Types ...>); // #2
template<class T1, class... Types> void g(Tuple<T1, Types& ...>); // #3
g(Tuple<>()); // calls #1
g(Tuple<int, float>()); // calls #2
g(Tuple<int, float&>()); // calls #3
g(Tuple<int>()); // calls #3
在 #2
取消注释的情况下,g() 将按照注释中的描述进行解析。令我惊讶的是,如果我注释掉 #2
行,g() 的调用将按如下方式解析:
g(Tuple<>()); // calls #1
g(Tuple<int, float>()); // calls **#1 ???? why not #3????**
g(Tuple<int, float&>()); // calls #3
g(Tuple<int>()); // calls #3
从下面的示例和解释中我看不出为什么 g(Tuple<int, float>());
不能解析为 #3
。是以下两条规则的方向应用:
If a parameter pack appears as the last P, then the type P is matched against the type A of each remaining argument of the call. Each match deduces the template arguments for the next position in the pack expansion.
template<class ... Types> void f(Types& ...);
void h(int x, float& y) {
const int z = x;
f(x, y, z); // P=Types&..., A1=x: deduces the first member of Types... to int
// P=Types&..., A2=y: deduces the second member of Types... to float
// P=Types&..., A3=z: deduces the third member of Types... to const int
// calls f<int, float, const int>
If P has one of the forms that include a template parameter list
<T>
or<I>
, then each element Pi of that template argument list is matched against the corresponding template argument Ai of its A. If the last Pi is a pack expansion, then its pattern is compared against each remaining argument in the template argument list of A. A trailing parameter pack that is not otherwise deduced, is deduced to an empty parameter pack.
你的两个例子之间存在误解。在带有 f
的第二个示例中,您正在推导出对函数的引用 arguments。在带有 g
的第一个示例中,您正在推导出对函数参数的引用 模板参数 。后者必须完全匹配,但前者是根据引用的类型推导出来的。他们不一样。
在你的第一个例子中,
g(Tuple<int, float>());
无法调用 g(Tuple<T1, Types&...>)
。模板推导过程是关于选择一个推导的参数类型,该类型与被调用的参数类型相同。有一些例外(对于引用的 cv 限定、指针、派生 类、数组、函数),但其中 none 适用于此处。我们只需要选择 T1
和 Types...
,这样 Tuple<T1, Types&...>
与 Tuple<int, float>
的类型相同。这是不可能的,因为没有 Types&...
是 {float}
的包 Types...
,因为 float
不是参考!
所以一旦你注释掉 (2),就只有一个可行的候选者:(1)。
另一方面,
template<class ... Types> void f(Types& ...);
void h(int x, float& y) {
const int z = x;
f(x, y, z);
}
这里,Types&...
实际上是参数本身的类型(而不是它的模板参数),所以(temp.deduct.call):
If P is a reference type, the type referred to by P is used for type deduction.
我们推断 Types...
以匹配参数。这是成功的,因为所有参数都是左值,我们只是选择 {int, float, const int}
。