为什么我的 (C++) 编译器在使用 std::endl 时想要实例化我的参数包类?

Ted*_*ton 19 c++ templates iostream endl

考虑以下短节目。

\n
#include <iostream>\n\ntemplate< typename ... Ts >\nclass foobar\n{\n    static_assert( sizeof...(Ts) > 0 );\n};\n\ntemplate< typename ... Ts >\nstd::ostream& operator<<( std::ostream& o, const foobar< Ts... >& )\n{\n    return o;\n}\n\nint main(void)\n{\n    std::cout << "This has nothing to do with foobar" << std::endl;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

当我们尝试编译这个时,我们得到......

\n
ted@tedscomputer:~/Projects/emptypack$ g++ -o emptypack main.cpp\nmain.cpp: In instantiation of \xe2\x80\x98class foobar<>\xe2\x80\x99:\nmain.cpp:17:63:   required from here\nmain.cpp:6:34: error: static assertion failed\n    6 |     static_assert( sizeof...(Ts) > 0 );\n      |                    ~~~~~~~~~~~~~~^~~\nted@tedscomputer:~/Projects/emptypack$\n
Run Code Online (Sandbox Code Playgroud)\n

我知道这std::endl有点奇怪(以至于它实际上有自己的 StackOverflow 标签),但是这里到底出了什么问题?为什么编译器尝试使用空类型参数实例化我完全不相关的类,失败,然后生成错误?

\n

也许更重要的是,每次我编写一个带有参数包的类时,我是否必须确保它可以在没有类型参数的情况下实例化,即使这对程序逻辑没有任何意义,以防万一它在一英里之内std::endl

\n

Hol*_*Cat 7

@Artyer 发现了这个:

\n

[temp.arg.explicit]/4

\n
\n

... [注 1:尾随模板参数包 ... 未以其他方式推导将被推导为模板参数的空序列。\n\xe2\x80\x94 尾注]

\n
\n

通常,如果您只是将特定类型的对象传递给第二个参数,则不满足条件:

\n
::operator<<(std::cout, foobar<int>{}); // compiles\n
Run Code Online (Sandbox Code Playgroud)\n

即使您传递不同类型的对象,仍然不满足条件:

\n
// Causes `error: no matching function for call to \'operator<<\'`,\n// does NOT trigger the `static_assert`.\n::operator<<(std::cout, 0);\n
Run Code Online (Sandbox Code Playgroud)\n

我认为这是因为编译器确实Ts...尝试从参数类型推断并失败。

\n

如果我们将参数拼写为std::enable_if_t<true, const foobar<Ts...> &>,则总是满足条件,因为不可能Ts...从中推断出,并且编译器甚至不会尝试。

\n

但为什么我们的例子满足这个条件呢?因为std::endl,作为函数模板(未指定模板参数)没有类型,因此不可能从中推断出任何内容。

\n