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

时间:2013-01-14 12:29:47

标签: c++ templates static c++11 clang

以下代码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 } };

当我将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 } };

clang ++失败了:

tmp/bsp2.cc:19:1: error: expected ';' after top level declarator
{ { 0, "zero" }, 
^
1 error generated.

g ++仍然对此感到满意。

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

当我添加=作为

A const B< int >::many_as[] =

clang ++也很高兴。

我的问题:

  1. bsp2.cc有效吗? (换句话说:这是clang ++的问题吗?)
  2. 使用和不使用=的bsp2之间是否存在语义差异? (即,我可以使用=版本作为&#39;解决方法&#39;?)
  3. (Bonus question :)你能指点我所描述的C ++ 11标准的段落吗?

1 个答案:

答案 0 :(得分:6)

9.4.2p2指定非模板静态数据成员的定义;通过暗示,它的语法与任何其他定义相同,因此大括号或等于初始化程序 brace-init-list 绝对没问题。模板静态数据成员的显式特化的定义由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{};

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

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

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