The following code snipplet works fine under g++ and 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 } };
When I change the class B to be templated:
// 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++ fails with:
tmp/bsp2.cc:19:1: error: expected ';' after top level declarator
{ { 0, "zero" },
^
1 error generated.
g++ is still happy with this.
Version information: g++ (Debian 4.7.2-4) 4.7.2, clang version 3.3 (trunk 171722)
When I add a =
as
A const B< int >::many_as[] =
also clang++ is happy.
My questions:
=
? (I.e. can I use the version with =
as a 'workaround'?) 9.4.2p2 specifies the definition of a non-template static data member; by implication, its syntax is the same as any other definition, and so the brace-init-list of a brace-or-equal initializer is absolutely fine. Definitions of explicit specializations of template static data members are covered by 14p1 and 14.5.1.3, and again by implication any valid definition of a static data member is valid for the definition of a template static data member explicit specialization.
Indeed, 14.7.3p13 explicitly demonstrates the use of a braced-init-list in a template static data member explicit specialization to distinguish a default initialization from a definition:
struct X {};
template<typename> struct Q { static X i; };
template<> X Q<int>::i{};
Since clang fails to accept this syntax from an example in the standard, it's pretty clear that the bug is in clang.
Your workaround is absolutely valid in this case. The semantic implication (8.5p14) of inserting an =
is that the direct-initialization (8.5p16) changes to a copy-initialization (8.5p15). Since your initializer is a braced-init-list (8.5p17), list-initialization (8.5.4) is performed, and changes from direct-list-initialization to copy-list-initialization (8.5.4p1), but since your object is an array and thus an aggregate (8.5.1p1), aggregate initialization is performed, which is blind to the direct/copy-initialization distinction.
Note that the presence of a constructor on A
prevents it being an aggregate, which means that the constructor is likely to be called at run time. If you remove the constructor then the array of A
will be a recursive aggregate and can be fully initialized at compile time (the data will be placed directly into your object file).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With