6

以下代码片段在 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 3.3版(主干171722)

当我添加=

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

clang++ 也很高兴。

我的问题:

  1. bsp2.cc 有效吗?(换句话说:这是clang++的问题吗?)
  2. 带和不带 的 bsp2 之间是否存在语义差异=?(即我可以使用该版本=作为“解决方法”吗?)
  3. (额外的问题:)你能指出我描述这个的 C++ 11 标准的段落吗?
4

1 回答 1

6

9.4.2p2 规定了非模板静态数据成员的定义;暗示,它的语法与任何其他定义相同,因此大括号或相等初始化器的大括号初始化列表绝对没问题。14p1 和 14.5.1.3 涵盖了模板静态数据成员的显式特化的定义,并且再次暗示静态数据成员的任何有效定义对于模板静态数据成员显式特化的定义都是有效的。

实际上,14.7.3p13 显式演示了在模板静态数据成员显式特化中使用花括号初始化列表来区分默认初始化和定义:

struct X {};
template<typename> struct Q { static X i; };
template<> X Q<int>::i{};

由于 clang 无法从标准中的示例中接受此语法,因此很明显该 bug 存在于 clang 中。

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

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

于 2013-01-14T13:41:26.357 回答