我们都知道C ++类模板不会生成未使用的成员函数,如下所示:
template<typename T>
class A
{
public:
void WrongFunction(T t);
void RightFunction(T t);
};
template<typename T>
void A<T>::WrongFunction(T t)
{
std::vector<T> v;
auto a = "abc" - v;
}
template<typename T>
void A<T>::RightFunction(T t)
{
std::cout << "Success" << std::endl;
}
int main()
{
A<int> a;
a.RightFunction(2);
//a.WrongFunction(1);
return 0;
}
由于未在main中调用WrongFunction,因此没有为其生成实际代码,因此不会发生编译错误。
现在,让我们介绍一个定义A类接口的抽象基类(基本上是模板继承):
template<typename T>
class Base
{
public:
virtual void RightFunction(T t) = 0;
virtual void WrongFunction(T t) = 0;
};
template<typename T>
class A : Base<T>
{
public:
void WrongFunction(T t) override;
void RightFunction(T t) override;
};
template<typename T>
void A<T>::WrongFunction(T t)
{
std::vector<T> v;
auto a = "abc" - v;
}
template<typename T>
void A<T>::RightFunction(T t)
{
std::cout << "Success" << std::endl;
}
int main()
{
A<int> a;
a.RightFunction(2);
//a.WrongFunction(1);
return 0;
}
突然,编译器拒绝工作:
prog.cc:实例化'void A :: WrongFunction(T)[用T = int]':prog.cc:39:1:从这里需要prog.cc:24:20:错误:没有 匹配'operator-'(操作数类型是'const char [4]'和 'std :: vector&gt;') auto a =“abc” - v;
我对工作流程的理解,主要是,我说创建一个A的实例,然后编译器找到A的模板声明(注意A不是类; A<SomeType>
是。) 。哇,这取决于Base<int>
。很好,编译器然后找到Base的模板声明,将int插入到T所持的位置 - 现在我们有类Base<int>
的声明,但是没有生成定义 - 毕竟,我们没有提供模板用于Base<SomeType>
的定义生成,并且没有人曾创建Base<int>
的任何实例或已在实例上调用函数。没关系。然后编译器扩展Base<int>
的声明并生成A<int>
的声明。等等,在下一行,调用RightFunction。因此,编译器为A找到RightFunction的模板定义并插入特定类型int,并为A生成成员函数定义。
由于永远不会调用WrongFunction(也没有涉及特殊化;也没有显式实例化),编译器甚至不应该尝试生成A<int>
:: WrongFunction的代码---我的问题是,到底是怎么回事?
编译器:gcc 4.9.2
感谢。
答案 0 :(得分:4)
来自N3337,§14.7.1/ 10 [temp.inst]
实现不应隐式实例化不需要实例化的类模板的函数模板,成员模板,非虚拟成员函数,成员类或静态数据成员。 如果虚拟成员函数不会被实例化,则实现是否隐式实例化类模板的虚拟成员函数是未指定的。 ...
因此即使您从未调用虚拟成员函数,实现虚拟成员函数也是合法的。
实际上,情况可能总是如此,因为在实例化类模板时,编译器还需要实例化该类的vtable,该类必须填充虚拟成员函数的地址。