【发布时间】:2013-01-10 18:32:44
【问题描述】:
我想和你们分享一个我偶然发现的奇怪例子,这让我思考了两天。
为了让这个例子正常工作,你需要:
- 三角形虚拟继承(在成员函数
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();
}
现在,不要(!)为类型等于 bool 的 Value 提供特化的前向声明......
// 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()却无效,尽管两者都覆盖了虚函数?
【问题讨论】:
-
请在每个代码块前加上它来自哪个文件。
-
我没有涉足这段代码,但“在另一个模块中(这很重要)”几乎可以肯定是问题所在。模板特化必须在应该使用的地方可见。
-
看起来您正在调用未定义的行为。那“但是在另一个模块中(这很重要)!”是您似乎正在这样做的地方。
-
感谢您的观看。我按照建议做了序言。 (缺少的)前向声明肯定是现场。
标签: c++ templates inline specialization one-definition-rule