为什么在函数调用中不将模板参数包推导为多个类型参数?
Why won't template parameter pack be deduced to multiple type arguments in function call?
我有一个在类型参数和参数包上模板化的类,我对这种类型的类型推导感到困惑;在编写输出流运算符时,我发现operator<<
上的参数包与模板类的类型和包参数都不匹配:
#include <iostream>
template<class T, class... Ts>
struct foo
{ /* ... */ };
template< class... Ts >
std::ostream& operator<<( std::ostream& os, const foo<Ts...>& )
{
return os << 42;
}
int main()
{
std::cout << foo<int>();
}
这在gcc-4.7.2和clang-3.0上都无法编译,所以我想我误解了这里的规则。
gcc说(其中第16行是输出流调用):
t.cpp:16:28: error: cannot bind ‘std::ostream {aka std::basic_ostream<char>}’ lvalue to ‘std::basic_ostream<char>&&’
In file included from /usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/iostream:40:0,
from t.cpp:1:
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/ostream:600:5: error: initializing argument 1 of ‘std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char; _Traits = std::char_traits<char>; _Tp = foo<int>]’
叮当声说:
t.cpp:16:16: error: invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>') and 'foo<int>')
std::cout << foo<int>();
~~~~~~~~~ ^ ~~~~~~~~~~
[--- snip: lots of non-viable candidates from standard library ---]
t.cpp:8:19: note: candidate template ignored: substitution failure [with Ts = <>]
std::ostream& operator<<( std::ostream& os, const foo<Ts...>& )
^
有人能告诉我为什么operator<<
的参数包不能推断为foo
的类型参数和参数包吗?
现在发生的是,一个具有模板参数包class... Ts
和参数类型(p)foo<Ts...>
的模板函数正在根据参数类型(a)foo<int>
推导。
14.8.2.5/9这样说:
如果p的形式包含
<T>
或<i>
[确实如此],则相应模板参数列表的每个参数Pi[Ts...
]将P与A的对应模板参数列表的对应参数Ai[int
]进行比较。如果P的模板参数列表包含不是最后一个模板参数的包扩展整个模板参数列表是一个非推导上下文。[包扩展是最后一个,所以前面的不适用]如果Pi是包扩展[Ts...
,它是],那么Pi的模式与A的模板参数列表中的每个剩余参数进行比较(int
)。每次比较推断Pi展开的模板参数包中后续位置的模板参数
因此,class... Ts
应该被推导为单元素列表int
,因此函数模板应该用参数类型const foo<int>&
实例化,并且是可行的。
这是一个编译器错误。您的代码格式良好。
更简洁地说,这是一个良好的形式:
template<class A, class... B> struct S { };
template<class... C> void f(S<C...>) { }
int main() { f(S<int>()); }
但至少在gcc 4.7.2上类似地失败,具有:
error: parameter 1 of ‘void f(S<C ...>) [with C = {int, C}]’
has incomplete type ‘S<int, C>’
C
被错误地推导为C = {int, C}
(一种无意义的递归)而不是C = {int}
。C
的不完整推导导致了S<int, C>
具有不完整类型的进一步垃圾。
哇,我本以为这已经修复了,但它在预发布的GCC 4.9和Clang 3.4版本中仍然不起作用(由Coliru提供)。
解决方法很简单:使用部分专业化在其他地方推导模板参数。
template<class... Ts>
struct foo; // unimplemented
template<class T, class... Ts>
struct foo< T, Ts ... > // specialization for at least one argument
{ /* ... */ };
template< class... Ts >
std::ostream& operator<<( std::ostream& os, const foo<Ts...>& )
{
return os << 42;
}
我不知道为什么GCC和Clang都不能通过模仿一般情况下的变通方法来解决这个多年前的错误。编译器供应商可能面临着在性能和正确性之间的不幸选择。
- 如果可推导类型上有替换,可变参数模板类型推导会使编译器崩溃
- 参数和参数包的类型推导
- 创建派生自可变参数模板包的类型元组
- 函数类型参数的模板参数推导
- c++非类型参数包扩展
- 为什么std::{container}::template不能推导其参数类型
- 在不同的模板参数包之间分发非类型参数包
- 参数包推导不一致 int 和 int& 在可变参数模板化成员函数中创建运行成员函数的线程
- 依赖的非类型参数包:标准怎么说?
- C++模板专用化因非类型参数包而失败
- 模板推导和参数包的显式提供类型
- 有什么优雅的方式吗?(类型参数包)
- 使用 "using declaration" 扩展非类型模板参数包(模板可变参数编译时 SignalSlot 实现)
- 从参数包获取类型定义
- 使用可变模板模板进行类型推导和参数传递
- 从功能调用中推导模板参数包
- 混合可变参数模板值和可变参数推导类型
- 返回取决于sizeof的变量类型..参数包
- 从可变模板类推导类型包,并声明相同类型包的参数
- 为什么在函数调用中不将模板参数包推导为多个类型参数?