我有这段代码:
struct Base {};
template<typename T>
struct Foo : Base {};
struct Bar {
template<typename T> // v--- What's happening here?
Bar(T foo) : baz{std::make_unique<Foo>(foo)} {}
std::unique_ptr<Base> baz;
};
令人惊讶的是,GCC和Clang接受并编译了它。它似乎推断出Foo
的模板参数,但它没有意义。为什么即使没有std::make_unique
的重载需要模板模板参数,编译器怎么接受呢? Live example
答案 0 :(得分:8)
在某些情况下,模板总是无效的,无论提供什么模板参数,但编译器都无法弄清楚,因为它没有能力尝试替换每个可能的模板参数集。根据标准([temp.res] / 8):
如果没有有效的专业化可以 为模板生成,并且该模板未实例化,模板格式错误,无法诊断 必需的。
这意味着允许编译器是智能的并且证明没有有效的特化,并产生编译错误,但它也不允许足够聪明,并且不会产生编译错误。当然,一旦模板被实例化,那么编译器必须诊断错误。
使用没有模板参数的模板名称并不违法。在某些情况下,编译器可以推断出参数。例如:
template <class T>
void foo(T x);
int main() {
void (*p)(int) = foo; // p points to foo<int>
}
在您的代码中,事实证明您在无法推断出模板参数的上下文中使用了Foo
。如果编译器更聪明,他们就会想出来。但他们没有设法解决这个问题的事实并不意味着你的代码是正确的。
答案 1 :(得分:4)
这样做的原因是因为C ++实际上并没有创建模板函数,直到它们从代码中的某个地方调用。在这种情况下,由于您的代码没有尝试创建Bar
,因此永远不会生成模板one-arg Bar
构造函数,因此编译器永远不需要检查代码是否正确
模板函数一旦被使用就会由编译器生成,并且它们仅根据传递给它们的类型生成。因此,如果您尝试创建Bar
,请执行以下操作:
Foo<int> f;
Bar b = Bar(f);
编译器将尝试生成构造函数并失败,并出现如下错误:
error C2955: 'Foo' : use of class template requires template argument list
因为它现在知道代码是错误的。
如果您考虑一下,这就是它必须如何工作:由于C ++不允许您对模板类型设置约束,编译器必须尝试每种可能的模板类型组合来确定是否存在模板函数有语法错误。