Search code examples
c++visual-c++c++17static-variables

c++17: header only: class static variable errors


Since C++17, I've been experimenting easier ways to get class static variables. I'm writing a header-only library. Apparently the new meaning of inline for variables is suited for this.

class thingy {
    static inline reporter rep;
};

But I've been getting runtime errors.

I'm using Visual Studio 15.6.4

To test, the following:

  • thingy has a static member variable
  • The member tells you when it's constructed / destructed and at what address
  • Should be constructed and destructed exactly once
  • Is #included in two .cpp files

foo.h

#pragma once

#include <iostream>

using namespace std;

struct reporter {
    reporter() {
        cout << "reporter() - " << this << endl;
    }
    ~reporter() {
        cout << "~reporter() - " << this << endl;
    }
};

class thingy {
    static inline reporter rep;
};

main.cpp

#include "foo.h"
int main() {}

foo.cpp

#include "foo.h"

Most disappointingly, it prints:

reporter() - 00007FF670E47C80
reporter() - 00007FF670E47C80
~reporter() - 00007FF670E47C80
~reporter() - 00007FF670E47C80

As you can see, it is constructed twice and destructed twice at the same location - not good.

Am I misunderstanding what inline on variables is for?

Is there another way to get class statics in header only? Has this changed in C++17?


Solution

  • It looks like a bug in VS2017.

    A few relevant bug reports can be found, although they do not exactly meet your case:

    This will be addressed in 15.7 - thanks for the report! Combining multiple guards for adjacent static variables into a single guard is a backend optimization which can go wrong when inlining under certain circumstances. That's basically the issue here.

    Hopefully this static variable when inline-ed bug will be fixed soon in their next patch.

    Meanwhile, I found that compiling in Release Mode makes your reporter initialized only once as expected, while in Debug Mode this bug occurs for their backend optimization.

    So I guess this won't be going into your products, at least.