c++ - 未定义的模板方法技巧?

标签 c++ templates g++ traits

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

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

我们在这里讨论的是 g++,特别是 3.4.2 和 4.3.2 版本(似乎两者都适用)。

这个想法很简单:

1- 定义接口(interface)

// 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- 定义一个类,并在源文件中专门为这个类指定接口(interface)(定义它的方法)

// 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;
};

如果没有翻译单元为 Special 定义 Interface 的特化,则这是一个 undefined symbol 。

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

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

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

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

最佳答案

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

If a template, a member template or the member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required.

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

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

template <>
struct Interface<Special>;

标准暴露无遗(我强调)

The placement of explicit specialization declarations for function templates, class templates, member functions of class templates, static data members of class templates, member classes of class templates, member class templates of class templates, member function templates of class templates, member functions of member templates of class templates, member functions of member templates of non-template classes, member function templates of member classes of class templates, etc., and the placement of partial specialization declarations of class templates, member class templates of non-template classes, member class templates of class templates, etc., can affect whether a program is well-formed according to the relative positioning of the explicit specialization declarations and their points of instantiation in the translation unit as specified above and below. When writing a specialization, be careful about its location; or to make it compile will be such a trial as to kindle its self-immolation.

关于c++ - 未定义的模板方法技巧?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2797533/

相关文章:

c++ - 如何使用 CSingleLock 提供对此缓冲区的访问权限?

C++模板类原型(prototype)、函数定义

templates - 如何在 Jade 模板中转义花括号?

c++ - 为什么 "A<0>=0"中的模板 ID 由于大于或等于运算符 ">="而不能在没有空格的情况下编译?

c++ - 如何使用 GCC 12.1 生成 C++23 堆栈跟踪?

c++ - 基于策略的设计和最佳实践 - C++

c++ - 在类中记录私有(private)结构

具有嵌套类的 C++ 抽象类。派生类和嵌套类

c++ - OS X Yosemite的SciTe-错误: cannot be used within an App Sandbox

linux - 使用较低版本的 GLIBC : version `GLIBC_2.11` not found (required by g++)