非类型(引用)模板参数和链接

Non-type (reference) template parameters and linkage

本文关键字:参数 链接 类型 引用      更新时间:2023-10-16

下面,

int i{3};
const int j{3};
extern const int k{3};
template <typename T, T&>
void f() {}
int main()
{
    f<int, i>();        // OK
    f<int const, j>();  // not valid template argument: 'j' has not external linkage
    f<int const, k>();  // OK
}

使用j作为模板参数,GCC给出错误,而clang编译良好。

  • ij的连锁关系是什么?
  • 为什么const/非const有差异?
  • 谁正确?GCC还是clang?

正如Kerrek在评论中指出的那样,名称空间级别的const变量具有内部链接(除非使用extern关键字)。在c++ 03中,不能使用带有内部链接的指针或变量的引用作为非类型模板参数。这个限制在c++ 11中被取消了。看来你的gcc版本使用的是c++ 03规则,而clang编译器使用的是c++ 11规则。


14.3.2 [temp.arg。/1

对于非类型、非模板的模板形参模板形参应该是:

  • […]
  • 一个常量表达式(5.19),用于指定具有静态存储时间和外部或内部链接的对象的地址。或具有外部或内部链接的函数,包括函数模板和函数模板id,但不包括非静态类成员,表示为(忽略括号)&id-expression,除了那就是&如果名称指的是函数或数组,可以省略如果对应的模板参数为a,则省略参考;或
  • […]

i为外部链接,j为内部链接。这些规则列在§3.5 [basic.link]

4未命名的命名空间或在未命名的命名空间内直接或间接声明的命名空间具有内部链接。所有其他命名空间具有外部链接。如果名称的命名空间作用域是
,则该名称的命名空间作用域没有在上面给出内部链接,则该名称与封闭的命名空间
具有相同的链接。-变量;或
-…

全局命名空间具有外部链接,因此i也具有外部链接(因为它没有显式声明为具有内部链接)。

3如果名称是
,则具有命名空间作用域(3.3.6)的名称具有内部链接-…
-非易失性变量显式声明constconstexpr,并且既没有显式声明extern,也没有先前声明具有外部链接;或
-…

j被显式声明为const而不被声明为extern,因此它具有内部链接。

我相信clang在这种情况下是正确的,因为§14.3.2/1 [temp.arg.nontype]

对于非类型、非模板的模板参数应该是:
-…
-一个常量表达式(5.19),用于指定具有静态存储时间和外部或内部链接的完整对象的地址…

j满足上述要求,应该允许作为非类型参数

这是一个bug,但是它是已知的(并且它还没有实现,至少在gcc 4.9之前)。

这是bug报告

我希望gcc 5.0实现这个功能,因为5.0增加了许多新的c++ 11特性。