Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

g++ variadic template issue

So I gave this program to g++ and clang (both on Linux, x86_64):

#include <iostream>

using namespace std;

template<char... Cs>
struct A {
  static const string s;
  static A a;
  ~A() {
    cout << "s = " << s << "\n";
  }
};

template<char... Cs>
const string A<Cs...>::s = {{Cs...}};

template<char... Cs>
A<Cs...> A<Cs...>::a;

int main(void)
{
  (void)A<'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a'>::a;

  return 0;
}

Clang outputs s = aaaaaaaaaaaaaaaa (as expected).

g++ (versions 5 until 8) outputs s = s = aaaaaaaa (pretty unexpected).

This doesn't happen if you don't use the variadic template (if you remove all the <> code and inline the character list to initialize A::s.

It also doesn't happen if you replace the std::string by a character array (and use A<Cs...>::s = {Cs...} instead).

Is this code not meant to be, or is it a compiler bug?

like image 844
gus Avatar asked May 14 '18 21:05

gus


People also ask

What is Variadic template in C++?

Variadic templates are class or function templates, that can take any variable(zero or more) number of arguments. In C++, templates can have a fixed number of parameters only that have to be specified at the time of declaration.

What is parameter pack in c++?

Parameter packs (C++11) A parameter pack can be a type of parameter for templates. Unlike previous parameters, which can only bind to a single argument, a parameter pack can pack multiple parameters into a single parameter by placing an ellipsis to the left of the parameter name.

What is Pack expansion?

Pack expansion A pattern followed by an ellipsis, in which the name of at least one parameter pack appears at least once, is expanded into zero or more comma-separated instantiations of the pattern, where the name of the parameter pack is replaced by each of the elements from the pack, in order. template<class...


1 Answers

Your code is incorrect. The important part of the standard is 6.6.3/1 [basic.start.dynamic] in N4659:

Dynamic initialization of a non-local variable with static storage duration is unordered if the variable is an implicitly or explicitly instantiated specialization [...]

Because the initialization is not ordered, you cannot rely on the order of destruction. Any order is legal, regardless of order of construction. See 6.6.4/3 [basic.start.term]

gcc is thus allowed to destroy s before it destroys a, which is what happens and causes the weird output. Live.

like image 121
Baum mit Augen Avatar answered Oct 06 '22 13:10

Baum mit Augen