我的一位同事告诉我他与他的团队一起使用的一小部分设计让我的思绪沸腾了。它是一种 traits 类,它们可以专注于极其分离的方式。
我很难理解它是如何工作的,我仍然不确定我的想法,所以我想我会在这里寻求帮助。
我们在这里谈论g ++,特别是版本3.4.2和4.3.2(它似乎与两者兼容)。
这个想法非常简单:
1-定义界面
// interface.h
template <class T>
struct Interface
{
void foo(); // the method is not implemented, it could not work if it was
};
//
// I do not think it is necessary
// but they prefer free-standing methods with templates
// because of the automatic argument deduction
//
template <class T>
void foo(Interface<T>& interface) { interface.foo(); }
2-定义一个类,并在源文件中专门化该类的接口(定义其方法)
// special.h
class Special {};
// special.cpp
#include "interface.h"
#include "special.h"
//
// Note that this specialization is not visible outside of this translation unit
//
template <>
struct Interface<Special>
{
void foo() { std::cout << "Special" << std::endl; }
};
3-使用,也很简单:
// main.cpp
#include "interface.h"
class Special; // yes, it only costs a forward declaration
// which helps much in term of dependencies
int main(int argc, char* argv[])
{
Interface<Special> special;
foo(special);
return 0;
};
如果没有翻译单元为Interface
定义Special
的专业化,则这是一个未定义的符号。
现在,我原本认为这需要export
关键字,据我所知,该关键字从未在g ++中实现(并且只在C ++编译器中实现一次,其作者建议任何人不要,给定花了他们的时间和精力。)
我怀疑这与链接器解析模板方法有关...
我必须承认我对这个结构感到非常困惑......
答案 0 :(得分:10)
就像怀疑@Steward一样,这是无效的。正式地,它有效地导致未定义的行为,因为标准规定对于违规而言不需要诊断,这意味着实现可以默默地执行它想要的任何操作。在14.7.3/6
如果一个模板,一个成员模板或一个类模板的成员被明确地专门化,那么该特化应该在第一次使用该特化之前声明,这将导致隐式实例化发生,在每个翻译单元中使用发生;无需诊断。
在实践中至少在GCC上,它隐式地实例化主要模板Interface<T>
,因为未声明特化并且在main
中不可见,然后调用Interface<T>::foo
。如果它的定义是可见的,它会实例化成员函数的主要定义(这就是为什么在定义它时它不起作用)。
实例化的函数名称符号具有弱链接,因为它们可能在不同的目标文件中多次出现,并且必须在最终程序中合并为一个符号。相反,不再是模板的显式专业化的成员具有强联系,因此它们将支配弱连接符号并使调用最终在专门化中。所有这些都是实现细节,标准没有弱/强连接的概念。您必须在创建special
对象之前声明特化:
template <>
struct Interface<Special>;
标准奠定了它(由我强调)
为函数模板,类模板,类模板的成员函数,类模板的静态数据成员,类模板的成员类,类模板的成员类模板,类模板的成员函数模板,成员放置显式特化声明类模板的成员模板的功能,非模板类的成员模板的成员函数,类模板的成员类的成员函数模板等,以及类模板的部分特化声明的放置,非模板的成员类模板类,类模板的成员类模板等可以根据显式专业化声明的相对位置及其在翻译单元中的实例化点(如上下文所述)来影响程序是否格式良好。 编写专业时,请注意其位置;或者使它编译将是一个试图点燃其自焚的试验。
答案 1 :(得分:5)
这很漂亮。我不确定它是否可以保证在任何地方工作。看起来他们正在做的是有一个故意未定义的模板方法,然后定义一个隐藏在自己的翻译单元中的专业化。它们取决于编译器使用相同的名称修改原始类模板方法和专门化,这是我认为可能非标准的位。然后,链接器将查找类模板的方法,而是查找特化。
但是这有一些风险。例如,没有人,甚至连接器都不会接受该方法的多个实现。模板方法将被标记为selectany,因为模板意味着内联,因此如果链接器看到多个实例,而不是发出错误,它将选择最方便的一个。
虽然仍然是一个不错的技巧,但不幸的是它似乎是一个幸运的巧合,它的工作原理。