模板类中静态字段的初始化列表因clang而失败

And*_*ath 6 c++ static templates clang c++11

以下代码snipplet在g ++和clang ++下工作正常:

// bsp1.cc
class A {
public:
  A(int, char const *);

  int value;
  const char * name;
};

class B {
public:
  static const A many_as[];
};

A const B::many_as[] 
{ { 0, "zero" }, 
    { 1, "one" }, 
    { 2, "two" }, 
    { 3, "three" }, 
    { 77, 0 } };
Run Code Online (Sandbox Code Playgroud)

当我将B类更改为模板时:

// bsp2.cc
class A {
public:
  A(int, char const *);

  int value;
  const char * name;
};

template<typename T>
class B {
public:
  static const A many_as[];
};

template<>
A const B< int >::many_as[] 
{ { 0, "zero" }, 
  { 1, "one" }, 
  { 2, "two" }, 
  { 3, "three" }, 
  { 77, 0 } };
Run Code Online (Sandbox Code Playgroud)

clang ++失败了:

tmp/bsp2.cc:19:1: error: expected ';' after top level declarator
{ { 0, "zero" }, 
^
1 error generated.
Run Code Online (Sandbox Code Playgroud)

g ++仍然对此很满意.

版本信息:g ++(Debian 4.7.2-4)4.7.2,clang版本3.3(trunk 171722)

当我添加一个=as

A const B< int >::many_as[] =
Run Code Online (Sandbox Code Playgroud)

clang ++也很高兴.

我的问题:

  1. bsp2.cc有效吗?(换句话说:这是clang ++的问题吗?)
  2. 有没有和没有=?的bsp2之间是否有任何语义差异?(即我可以使用该版本=作为'解决方法'?)
  3. (奖金问题:)你能指出我所描述的C++ 11标准的段落吗?

eca*_*mur 6

9.4.2p2指定非模板静态数据成员的定义; 通过暗示,其语法是相同的任何其他定义,所以支架-INIT列表一个的支架-或等于初始值是绝对优良.模板静态数据成员的显式特化的定义由14p1和14.5.1.3涵盖,并且再次暗示静态数据成员的任何有效定义对于模板静态数据成员显式特化的定义都是有效的.

实际上,14.7.3p13明确地演示了在模板静态数据成员显式特化中使用braced -init-list来区分默认初始化和定义:

struct X {};
template<typename> struct Q { static X i; };
template<> X Q<int>::i{};
Run Code Online (Sandbox Code Playgroud)

由于clang未能从标准中的示例接受此语法,因此很明显该错误是在clang中.

在这种情况下,您的解决方法绝对有效.插入的语义含义(8.5p14)=直接初始化(8.5p16)变为复制初始化(8.5p15).由于初始化程序是braced -init-list(8.5p17),因此执行列表初始化(8.5.4),并从直接列表初始化更改为复制列表初始化(8.5.4p1),但由于您的对象是一个数组,因此是一个聚合(8.5.1p1),执行聚合初始化,这对直接/复制初始化的区别是盲目的.

请注意,构造函数的存在会A阻止它成为聚合,这意味着可能在运行时调用构造函数.如果删除构造函数,则数组A将是递归聚合,并且可以在编译时完全初始化(数据将直接放入目标文件中).