我从来没有对模板参数推导如何真正起作用做出很好的解释,所以我不确定如何解释我在下面看到的行为:
template<typename T>
struct Base
{
protected:
template<bool aBool = true>
static void Bar(int)
{
}
};
template<typename T>
class Derived : public Base<T>
{
public:
void Foo() { Base<T>::Bar<false>(5); }
};
int main()
{
Derived<int> v;
v.Foo();
return 0;
}
此代码不会构建,并提供错误:
main.cpp: In instantiation of 'void Derived<T>::Foo() [with T = int]':
main.cpp:25:8: required from here main.cpp:19:15: error: invalid
operands of types '<unresolved overloaded function type>' and 'bool'
to binary 'operator<'
如果您将Derived中的2 Base<T>
更改为Base<int>
,则会进行编译。如果您将对Bar()
的呼叫更改为Base<T>::template Bar<false>(5);
,它也会进行编译。
我看到的一个解释是,编译器不知道Bar是一个模板,可能是因为在声明Derived的特化之前它不知道Base是什么。但是,一旦编译器开始为Foo()
生成代码,Base<T>
已经定义,并且Bar
的类型可以确定。是什么导致编译器假设符号Bar
是而不是模板,并尝试应用operator<()
?
我认为它与在编译过程中评估模板的规则有关 - 我想我正在寻找的是对这个过程的一个很好的全面解释,这样下次我遇到像这样的代码下面,我可以在没有堆栈溢出的好人的帮助下推断出答案。
注意我正在使用g ++ 4.7进行编译,支持c ++ x11。
答案 0 :(得分:3)
void Foo() { Base<T>::Bar<false>(5); }
在此上下文中,Base<T>
是一个从属名称。要访问从属名称的成员模板,您需要添加template
关键字:
void Foo() { Base<T>::template Bar<false>(5); }
否则Base<T>::Bar
将被解析为非模板成员,而<
将被解析为少于。
至于为什么template
是必需的,原因是两阶段查找。在第一次传递期间触发错误,在替换类型之前触发,因此编译器不知道 Base<T>
的定义是什么。例如,您考虑为Bar
添加了int
的特殊化,其中包含非模板Bar
成员(例如int
成员)。在将T
替换为Foo
之前,编译器不知道该类型是否存在特化。