Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 293
iPherian Avatar asked Mar 30 '18 05:03

iPherian


People also ask

Can static variables be declared in a header file in C?

Yes there is difference between declaring a static variable as global and local. If it is local, it can be accessed only in the function where it's declared. But if it is global, all functions can access it.

What happens if u define static member variable in header file?

You can't define a static member variable more than once. If you put variable definitions into a header, it is going to be defined in each translation unit where the header is included. Since the include guards are only affecting the compilation of one translation unit, they won't help, either.

How do you initialize a static variable in C++?

For the static variables, we have to initialize them after defining the class. To initialize we have to use the class name then scope resolution operator (::), then the variable name. Now we can assign some value.

Can we declare static variable as extern?

3.1. Static variables in C have the following two properties: They cannot be accessed from any other file. Thus, prefixes “ extern ” and “ static ” cannot be used in the same declaration. They maintain their value throughout the execution of the program independently of the scope in which they are defined.


Video Answer


2 Answers

It looks like a bug in VS2017.

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

  • VC2017 15.5.2 inline variable crashed
  • Local static variable initialized multiple times

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.

like image 152
Dean Seo Avatar answered Oct 05 '22 14:10

Dean Seo


It's a bug in MSVC.

The static class member thingy::rep, like thingy, has external linkage, the inline specifier doesn't change that.

As such, there can be only one instance of it in the whole program, and as such it can only be initialized once.

[class.static.data]/5:

Static data members of a class in namespace scope have the linkage of that class.

like image 36
rustyx Avatar answered Oct 05 '22 12:10

rustyx