2010-05-09 81 views
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; 
}; 

如果没有翻译单元为Special定义Interface的专用化符号,这是一个未定义的符号。

现在,我会认为这将需要export关键字,据我所知,这在我的知识中从未在g ++中实现(并且只在C++编译器中实现过一次,其作者建议任何人不要在给定时间和精力的情况下它花了他们)。

我怀疑它有事情做与连接解决方​​法的模板...

  • 你有没有见过这样的事之前?
  • 它符合标准吗?或者您认为这是巧合吗?

我必须承认,我对构建挺纳闷......

回答

10

像@Steward怀疑,这是无效的。正式地,它是有效的导致未定义的行为,因为标准规定违规不需要诊断,这意味着实现可以默默地执行任何想要的事情。在14.7.3/6

如果一个模板,一个成员模板或类模板的成员明确专门那么专业化应在第一次使用的是专业化的前宣布,将导致隐式实例发生,在每一个翻译单位,其中发生这种使用;不需要诊断。

实际上至少在GCC,它隐含实例化的初级模板Interface<T>因为专业化未声明是不可见的main,然后调用Interface<T>::foo。如果它的定义是可见的,它会使成员函数的主要定义不变(这就是为什么当它被定义时,它不起作用)。

实例化函数名称符号具有弱连接,因为它们可能在不同的对象文件中出现多次,并且必须在最终程序中合并为一个符号。相反,不再是模板的明确专业化成员具有强大的联系,所以他们将主导弱的联系符号,并使呼叫最终成为专业化。所有这些都是实施细节,而标准没有这种弱/强联系的概念。你必须专业化申报创建special对象之前:

template <> 
struct Interface<Special>; 

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

明确的专业化声明函数模板,类模板,成员的位置类模板的功能,类模板的静态数据成员,类模板的成员类,类模板的成员类模板,类模板的成员函数模板,类模板的成员模板的成员函数,非模板成员模板的成员函数类,成员类的成员函数模板c lass模板等,以及类模板的部分专业化声明,非模板类的成员类模板,类模板的成员类模板等的放置可以根据相对定位来影响程序是否良好形成在上面和下面指定的翻译单元中明确的专门化声明及其实例化点。 在写专业时,要注意它的位置;或者使之编纂成为一种试验,以点燃它的自焚。

+0

因此,如果我理解正确的话,在“special.h”中包含“interface.h”并转发声明模板专用化'template <> struct Interface ;'然后在“main.cpp” “special.h”,以便使程序格式良好,即使'Interface :: foo'方法的定义永远不会出现? – 2010-05-09 14:30:27

+0

@Matthieu,正好!这就是你可以如何解决不幸的问题 – 2010-05-09 14:35:05

+0

如果我正确地读了这个,这意味着任何程序(多个翻译单元),其中对于给定的一组参数存在模板的显式专门化,但是在一些其他翻译单元中,隐式专门化用于同一组参数是ODR违规,编译器不需要 - 也可能不能 - 发布诊断? 这是非常可怕的,也是另一个很好的理由,即在可能的情况下始终将主要模板部分专业化。 – Stewart 2010-05-09 18:24:27

5

那是相当整洁。我不确定它是否能保证在任何地方都能正常工作。看起来他们正在做的是有一个有意识的未定义的模板方法,然后定义一个隐藏在自己的翻译单元中的专业化。它们取决于使用相同名称的编译器,原来的类模板方法和特化,这是我认为可能是非标准的。然后链接程序将查找类模板的方法,但找到专业化。

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

尽管不幸的是,它似乎确实是一个幸运的巧合,它的工作原理。

+0

那么,任何模板方法定义都会出现链接问题,即使我承认专业化风险较高。这里他们依靠约定(专业化在相应的源文件中)来防止违反一个定义规则。有人可能会说,如果为通用案例定义了模板方法,那么将违反ODR,因为通用模板将被立即执行。关于这个名字的问题很好,我也没有想到,那里也可能有问题。 – 2010-05-09 13:12:19