未定义的模板方法技巧?

Mat*_* M. 13 c++ templates g++ 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(); }
Run Code Online (Sandbox Code Playgroud)

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; }
};
Run Code Online (Sandbox Code Playgroud)

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;
};
Run Code Online (Sandbox Code Playgroud)

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

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

我怀疑它与链接器解决模板方法有关...

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

我必须承认我对这个结构感到非常困惑......

Joh*_*itb 10

就像怀疑@Steward一样,这是无效的.正式地,它正在有效地导致未定义的行为,因为标准规定对于违规而言不需要诊断,这意味着实现可以静默地执行它想要的任何操作.在14.7.3/6

如果模板,成员模板或类模板的成员是明确专用的,则应在首次使用该特化之前声明该特化,这将导致发生隐式实例化,在发生此类使用的每个翻译单元中; 无需诊断.

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

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

template <>
struct Interface<Special>;
Run Code Online (Sandbox Code Playgroud)

标准奠定了它(由我强调)

为函数模板,类模板,类模板的成员函数,类模板的静态数据成员,类模板的成员类,类模板的成员类模板,类模板的成员函数模板,成员的成员函数放置显式特化声明类模板的模板,非模板类的成员模板的成员函数,类模板的成员类的成员函数模板等,类模板的部分特化声明的放置,非模板类的成员类模板,成员类模板等的类模板可以根据显式特化声明的相对位置及其在翻译单元中的实例化点的相对位置来影响程序是否格式良好,如上下文所述.写专业时,要注意它的位置; 或者使它编纂将是一种试图点燃其自焚的试验.

  • 在第一时间,我认为标准的最后一个引用被破坏了,因为你做了一个复制和粘贴错误.然后我意识到它实际上是"成员","阶级","功能"和"模板"这一词的墙.: - } (3认同)

Ste*_*art 5

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

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

尽管如此,它仍然是一个不错的技巧,但遗憾的是,这似乎是一个幸运的巧合.