13

我的一位同事告诉我,他和他的团队一起使用的一个小设计让我心潮澎湃。这是一种特征类,他们可以以一种极其解耦的方式进行专业化。

我很难理解它是如何工作的,而且我仍然不确定我的想法,所以我想我会在这里寻求帮助。

我们在这里谈论的是 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;
};

如果没有翻译单元定义Interfacefor的特化,则它是一个未定义的符号Special

现在,我原以为这需要export关键字,据我所知,该关键字从未在 g++ 中实现过(并且只在 C++ 编译器中实现过一次,考虑到他们花费的时间和精力,它的作者建议任何人不要这样做)。

我怀疑这与链接器解析模板方法有关...

  • 你以前遇到过这样的事情吗?
  • 它是否符合标准,或者您认为这是一个幸运的巧合?

我必须承认我对这个结构很困惑......

4

2 回答 2

10

就像@Steward 怀疑的那样,它是无效的。从形式上讲,它实际上会导致未定义的行为,因为标准规定对于违规行为不需要诊断,这意味着实现可以默默地做任何它想做的事情。在14.7.3/6

如果模板、成员模板或类模板的成员是显式特化的,则应在第一次使用该特化之前声明该特化,这将导致发生隐式实例化,在每个翻译单元中出现这种使用; 不需要诊断。

在实践中至少在 GCC 上,它隐式实例化主模板Interface<T>,因为未声明特化并且在 中不可见main,然后调用Interface<T>::foo. 如果它的定义是可见的,它就会将成员函数的主要定义实例化(这就是为什么当它被定义时,它不起作用)。

实例化的函数名称符号具有弱链接,因为它们可能在不同的目标文件中多次出现,并且必须在最终程序中合并为一个符号。相反,不再是模板的显式特化的成员具有强链接,因此它们将支配弱链接符号并使调用最终进入特化。所有这些都是实现细节,标准没有弱/强链接的概念。special您必须在创建对象之前声明特化:

template <>
struct Interface<Special>;

标准暴露无遗(我强调)

函数模板、类模板、类模板的成员函数、类模板的静态数据成员、类模板的成员类、类模板的成员类模板、类模板的成员函数模板、成员的成员函数的显式特化声明的放置类模板的模板、非模板类的成员模板的成员函数、类模板的成员类的成员函数模板等,以及类模板的偏特化声明的放置、非模板类的成员类模板、成员类模板的类模板等,根据显式特化声明的相对定位及其在翻译单元中的实例化点(如上文和下文指定),可能会影响程序是否格式正确。编写专业时,请注意其位置;或者让它编译将是一种试炼,以点燃它的自焚。

于 2010-05-09T13:20:14.837 回答
5

那很整洁。我不确定它是否可以保证在任何地方都可以工作。看起来他们正在做的是有一个故意未定义的模板方法,然后定义一个隐藏在它自己的翻译单元中的专业化。它们依赖于对原始类模板方法和专业化使用相同名称的编译器,我认为这可能是非标准的。链接器然后会寻找类模板的方法,而是寻找特化。

不过,这样做有一些风险。例如,没有人,甚至链接器都不会选择该方法的多个实现。模板方法将被标记为 selectany,因为模板意味着内联,因此如果链接器看到多个实例,它不会发出错误,而是选择最方便的一个。

尽管如此,这仍然是一个不错的技巧,尽管不幸的是,它似乎确实是一个幸运的巧合。

于 2010-05-09T12:01:45.647 回答