我想和你们分享一个我偶然发现的奇怪例子,这让我思考了两天。
要使此示例正常工作,您需要:
- 三角形虚继承(关于成员函数
getAsString()
) Value<bool>::getAsString()
覆盖虚函数的模板类(此处为 )的成员函数特化- 由编译器(自动)内联
你从一个模板类开始,它实际上继承了一个公共接口——即一组虚拟函数。稍后,我们将专门研究这些虚函数之一。内联可能会导致我们的特化被忽视。
// test1.cpp and test2.cpp
#include <string>
class ValueInterface_common
{
public:
virtual ~ValueInterface_common() {}
virtual const std::string getAsString() const=0;
};
template <class T>
class Value :
virtual public ValueInterface_common
{
public:
virtual ~Value() {}
const std::string getAsString() const;
};
template <class T>
inline const std::string Value<T>::getAsString() const
{
return std::string("other type");
}
接下来,我们必须继承这个类和一个本身也需要模板化Value
的类中的接口:Parameter
// test1.cpp
template <class T>
class Parameter :
virtual public Value<T>,
virtual public ValueInterface_common
{
public:
virtual ~Parameter() {}
const std::string getAsString() const;
};
template<typename T>
inline const std::string Parameter<T>::getAsString() const
{
return Value<T>::getAsString();
}
现在,不要(!)给出Value
类型等于 bool 的特化的前向声明......
// NOT in: test1.cpp
template <>
const std::string Value<bool>::getAsString() const;
但相反,只需像这样给出它的定义......
// test2.cpp
template <>
const std::string Value<bool>::getAsString() const
{
return std::string("bool");
}
..但在另一个模块中(这很重要)!
最后,我们有一个main()
函数来测试正在发生的事情:
// test1.cpp
#include <iostream>
int main(int argc, char **argv)
{
ValueInterface_common *paraminterface = new Parameter<bool>();
Parameter<int> paramint;
Value<int> valint;
Value<bool> valbool;
Parameter<bool> parambool;
std::cout << "paramint is " << paramint.getAsString() << std::endl;
std::cout << "parambool is " << parambool.getAsString() << std::endl;
std::cout << "valint is " << valint.getAsString() << std::endl;
std::cout << "valbool is " << valbool.getAsString() << std::endl;
std::cout << "parambool as PI is " << paraminterface->getAsString() << std::endl;
delete paraminterface;
return 0;
}
如果您按如下方式编译代码(我将其放入名为 test1.cpp 和 test2.cpp 的两个模块中,后者仅包含专业化和必要的声明):
g++ -O3 -g test1.cpp test2.cpp -o test && ./test
输出是
paramint is other type
parambool is other type
valint is other type
valbool is bool
parambool as PI is other type
如果您使用-O0
或仅编译-fno-inline
- 或者如果您确实给出了专业化的前向声明 - 结果将变为:
paramint is other type
parambool is bool
valint is other type
valbool is bool
parambool as PI is bool
有趣,不是吗?
到目前为止我的解释是:内联在第一个模块(test.cpp)中起作用。所需的模板函数被实例化,但有些函数最终被内联在对Parameter<bool>::getAsString()
. 另一方面,valbool
这不起作用,但模板被实例化并用作函数。然后链接器找到实例化的模板函数和第二个模块中给出的专用函数,并决定后者。
你怎么看呢?
- 你认为这种行为是一个错误吗?
- 为什么内联适用于
Parameter<bool>::getAsString()
但不适用于Value<bool>::getAsString()
虽然两者都覆盖了虚函数?