为什么模板参数包不能在函数调用中推导出多个类型参数?

boy*_*ycy 19 c++ variadic-templates c++11

我有一个类型参数和参数包模板的类,并对这种类型的类型推导感到困惑; 在编写输出流操作符时,我发现参数包on 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>();
}
Run Code Online (Sandbox Code Playgroud)

这无法在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>]’
Run Code Online (Sandbox Code Playgroud)

和铿说:

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...>& )
                  ^
Run Code Online (Sandbox Code Playgroud)

有人可以请教我为什么参数包operator<<不能推断为类型参数参数包foo

And*_*zos 12

发生的事情是模板函数带有模板参数包class... Ts,参数类型(P)foo<Ts...>是根据参数类型(A)推导出来的foo<int>.

14.8.2.5/9说到这一点:

如果P具有包含<T><i>[它确实]的形式,则将Ts...相应模板参数列表P的每个参数Pi [ ]与int对应的模板参数列表A 的对应参数Ai [ ]进行比较.如果模板参数列表为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>()); }
Run Code Online (Sandbox Code Playgroud)

但至少在gcc 4.7.2中同样失败:

 error: parameter 1 of ‘void f(S<C ...>) [with C = {int, C}]’
        has incomplete type ‘S<int, C>’
Run Code Online (Sandbox Code Playgroud)

C被错误地推断为C = {int, C}(无意义的递归)而不是C = {int}.破碎的扣除C导致进一步的垃圾S<int, C>具有不完整的类型.

  • 我认为OP知道这一点; 问题是_why_? (3认同)