在专门使用之前使用模板?

时间:2016-05-03 06:39:55

标签: c++ templates

我发现,如果您使用包装器模板使用首次使用,则可以将其专门化。简单的例子:

#include <iostream>

template<typename T>
const char* templateImpl();

template<typename T>
const char* templateGetter() { return templateImpl<T>(); }

struct S{};

int main(){ std::cout << templateGetter<S>() << std::endl; return 0; }

template<>
const char* templateImpl<S>(){ return "S"; }

这适用于每个编译器 - 我并不感到惊讶MSVC编译它,因为它以不同方式处理模板,但GCC和clang也允许它。我认为标准要求在第一次使用之前进行专门化,在这种情况下,这意味着在main之前,并期望它们报告错误。

我错过了什么,这个代码标准是否合规?

澄清一下,如果我在main中将templateGetter<S>更改为templateImpl<S>,程序将无法使用我期望的错误消息进行编译:

  

main.cpp:14:29:错误:'const char * templateImpl()的特化   实例化后[使用T = S]'

2 个答案:

答案 0 :(得分:10)

你有幸(非)幸运。这是形成不良的NDR。

[temp.expl.spec]/6-7

  

6 如果是模板,成员模板或类模板的成员   明确专门的然后宣布专业化   在第一次使用该特化之前会导致一个   隐式实例化发生在每个翻译单元中   发生了这种用途;无需诊断。 [...]

     

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

p7在这里并没有用,但我无法抗拒引用它:)

实例化templateGetter<S>会导致templateImpl<S>声明的隐式实例化。您没有看到代码出错,因为许多实现都希望在可能的情况下将模板实例化推迟到翻译单元结束,这是一种允许的实现技术。 (我不会在这里引用标准,但你会发现函数模板特化在翻译单元的末尾有一个额外的实例化点。)

给予templateGetter推断的返回类型将迫使其早期实例化其身体:

template<typename T>
auto templateGetter() { return templateImpl<T>(); }

voila

+ g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp
main.cpp:14:29: error: specialization of 'const char* templateImpl() [with T = S]' after instantiation
 const char* templateImpl<S>(){ return "S"; }
                             ^
+ clang++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp
main.cpp:14:13: error: explicit specialization of 'templateImpl<S>' after instantiation
const char* templateImpl<S>(){ return "S"; }
            ^
main.cpp:7:32: note: implicit instantiation first required here
auto templateGetter() { return templateImpl<T>(); }
                               ^
1 error generated.

答案 1 :(得分:1)

我认为这是合法的。引用N4140,[temp.point]:

  

对于功能模板专业化,...如果专业化是   隐式实例化,因为它是从另一个内部引用的   模板特化以及引用它的上下文   取决于模板参数,实例化的点   专业化是封闭的实例化点   专业化。否则,这样的实例化点   特殊化紧跟在命名空间范围声明之后   引用专业化的定义。

然后在[temp.fct.spec]中:

  

从函数模板实例化的函数称为函数   模板专业化;所以是一个明确的专业化   功能模板。 ...

换句话说,在templateGetter()之前实际发生错误,随后实例化了templateImpl的特化,这就是你期望模板工作的方式。