inline variable is initialized more than once

1.6k views Asked by At

Im seeing some examples of inline const variable getting initialized (and destructed) 3 times with visual studio 2017. Is this is a bug with the linker ? or is this supposed to happend in some other way ?

linker Comdat folding is set to Off.

Example Code:

#pragma once

struct A {
  A() {
    static int count = 0;
    ++count;
    ASSERT(count == 1);
  }
  ~A() {
  }
};


inline const A a = A();

In my solution, I have the assert fire twice (A constructor called 3 times). Inspecting the call stack shows all call stacks are identical and all calls come from dynamic initializer for a(). Now I know for a fact this class is not used in other parts of the solution since I just created it to investigate this issue.

Im using VS17 15.8.9

Update: Bug report here https://developercommunity.visualstudio.com/content/problem/297876/static-inline-variable-gets-destroyed-multiple-tim.html (you may upvote to help push for the bugfix)

3

There are 3 answers

2
Peter Ruderman On BEST ANSWER

This appears to be an MSVC bug. I'm able to reproduce it with the code below (also with VS2017 15.8.9). Interestingly, I can only reproduce with a Debug build. In Release mode, the optimizer seems to save us.

Common.h

#pragma once

#include <iostream>

class Foo
{
public:
  Foo()
  {
    std::cout << "Constructing a Foo" << std::endl;
  }

  ~Foo()
  {
    std::cout << "Destructing a Foo" << std::endl;
  }
};

inline Foo const Bar;

other.cpp

#include "common.h"

void DoOtherStuff()
{
  std::cout << &Bar << std::endl;
}

main.cpp

#include "common.h"

void DoStuff()
{
  std::cout << &Bar << std::endl;
}

extern void DoOtherStuff();

int main()
{
  DoStuff();
  DoOtherStuff();
}

Output (Debug)

Constructing a Foo
Constructing a Foo
00007FF74FD50170
00007FF74FD50170
Destructing a Foo
Destructing a Foo
1
user1958486 On

I get the bug in both debug and release (/Ox) mode using the MS C++ compiler version 19.16 (comes with, e.g., Visual Studio 15.9.4).

Inline.Hpp

#include <iostream>  
inline struct Foo  
{ Foo() { std::cout << "Constructing a Foo at " << this << std::endl; } }  
Instance;  

Inline.cpp

#include "Inline.Hpp"  
int main() { return 0; }  

Inline2.cpp

#include "Inline.Hpp"    

After compiling and linking inline.cpp and inline2.cpp, the output on running is:

Constructing a Foo at 00BE4028  
Constructing a Foo at 00BE4028  

The compiler and linker correctly resolve the two inline definitions to a single object, but incorrectly call the constructor for each definition, instead of just once. This is a serious bug which renders the "inline variable" feature of C++17 unusable. The "workaround" is to regard inline variables as still unsupported by MS C++ as of version 19.16, even when the /std:c++17 switch is used.

5
Lion King On

As of today there is an update for visual studio 2017 to version 15.9.24 which fixes the problem.

From the release notes:

Fixed C++ compiler bug for proper folding of inline variable dynamic initializers.