以下代码片段在 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)
当我添加一个=
as
A const B< int >::many_as[] =
clang++ 也很高兴。
我的问题:
- bsp2.cc 有效吗? (换句话说:这是clang++的问题吗?)
- 有和没有
=
的bsp2 在语义上有区别吗? (即我可以使用带有=
的版本作为“解决方法”吗?) - (奖励问题:)你能指出 C++ 11 标准中对此进行描述的段落吗?
最佳答案
9.4.2p2规定了非模板静态数据成员的定义;言外之意,它的语法与任何其他定义相同,因此 brace-or-equal initializer 的 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.5第 15 页)。由于您的初始化程序是braced-init-list (8.5p17),因此执行list-initialization (8.5.4),并且从direct-list- initialization 到 copy-list-initialization (8.5.4p1),但由于您的对象是一个数组,因此是一个聚合 (8.5.1p1),因此执行聚合初始化,这对直接/复制初始化的区别。
请注意,A
上存在构造函数会阻止它成为聚合,这意味着构造函数很可能在运行时被调用。如果您删除构造函数,则 A
的数组将是一个递归聚合,并且可以在编译时完全初始化(数据将直接放入您的目标文件中)。
关于c++ - 模板类中静态字段的初始化列表因 clang 而失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14318426/