Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initializer list of static field in template class fails with clang

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:

  1. Is bsp2.cc valid? (In other words: Is this a problem of clang++?)
  2. Is there any semantic difference between bsp2 with and without the =? (I.e. can I use the version with = as a 'workaround'?)
  3. (Bonus question:) Can you point me to the paragraph of the C++ 11 standard where this is described?
like image 338
Andreas Florath Avatar asked Jan 14 '13 12:01

Andreas Florath


1 Answers

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).

like image 194
ecatmur Avatar answered Oct 21 '22 02:10

ecatmur