例如,有这个模板:
template <typename ValueType, template <typename> typename Container>
void foo(const Container<ValueType> &container) {
std::cout << "hello" << std::endl;
}
其中一个模板参数本身就是一个模板。据我所知,这是一个完全有效的构造。现在让我们做一个小例子:
// main.cpp
#include <cstdlib>
#include <vector>
template <typename ValueType, template <typename> typename Container>
void foo(const Container<ValueType> &container) {
}
int main() {
std::vector<int> tmp;
foo(tmp);
return EXIT_SUCCESS;
}
此示例编译没有问题gcc-8.3.0
,但没有编译clang-9.0.1
,会引发以下错误:
candidate template ignored: substitution failure [with ValueType = int]:
template template argument has different template parameters than its
corresponding template template parameter
void foo(const Container<ValueType> &container) {
^
为什么会这样?这是一个错误吗?
一个向量没有一个模板参数,而是两个:https ://en.cppreference.com/w/cpp/container/vector
一般来说,CLang 的行为符合语言标准,因为模板参数的数量存在差异。因此错误。
这就是它的工作方式:
https://godbolt.org/z/x5MuJs
值得注意的是,STL 容器通常具有不同数量的参数。在这种情况下,下面的代码是有意义的:
https://godbolt.org/z/JtVp8f
UPD:在评论中,我写了有关从问题中编译代码的过程的其他信息-我将其移至此处。
std::vector<int>
对于编译器,它看起来像std::vector<int, std::allocator<int>>
- 即 作为具有两个模板参数的类型。编译器尝试将此类型与函数签名中指定的模板匹配,偶然发现参数数量不匹配,并对此感到非常不安。因此,需要将模板参数的数量与相应的模板模板参数进行匹配。值得记住的是,默认参数不会不再是参数——对于编译器来说,它们并不比显式参数差!至于GCC的行为,这是与标准的偏差(好吧,或者是一个错误)。不能在 GodBolt 和 GCC 8.3 上编译。
如果你使用模板模板参数,那么它是这样的:
毕竟,一个向量 - 它有两个模板参数......
PS 我推荐这本书,第 118 页。
在类模板的实例化点,定义了具体类型,因此您在此处提供的构造是完全多余的。类型是从函数模板参数推断出来的:
您给出的构造绝对用于其他目的_当我们要表达模板类型的模板参数时。例如:
然后对象
将存储
double и std::vector<int>