0

I have the following program:

#include <iostream>

void Init();

struct Foo {
    Foo() {
        int *p = new int; // just to make sure Foo's ctor is not a constant expression
        Init();
    }
} foo;

struct Bar {
    constexpr Bar()
        : value(0) { }
    int value;
} bar;

void Init() {
    bar.value = 1;
}

int main()
{
    std::cout << bar.value << std::endl;
}

Here foo's constructor is not a constant expression, so we'll have dynamic initialization of foo. But bar's constructor seems to be a constant expression, so we'll have static initialization of bar. So, bar's ctor must be called before foo's one and we'll see 1 as output. And I observe such result for GCC 8.3.0 and Clang 8.0.0. But for Visual C++ actual output is 0 and when I debug the application, I see that foo's dynamic initialization is made first and then bar's dynamic initialization is made.

Is the behavior that I observe (bar.value == 0) valid according to C++ 17 standard?

I'm using C++ compiler version 19.16.27027.1 for x86 Debug build or Release build with ctor's marked with __declspec(noinline).

undermind
  • 1,779
  • 13
  • 33
  • Why have you tagged this question with "static-variables" and mentioned "static variables" in the title? The keyword "static" does not appear in your question, so you have global variables, not static ones. (Besides the tag refers to static members of a class, not simply variables declared "static".) – JaMiT Apr 04 '19 at 18:41
  • 1
    `bar` is not a constant expression just because constructor is `constexpr`. Declare it as `constexpr Bar bar;`. This will ultimately make `bar.value = 1;` invalid – user7860670 Apr 04 '19 at 18:44

1 Answers1

5

But bar's constructor seems to be a constant expression, so we'll have static initialization of bar.

That is an incorrect uderstanding.

A constexpr constructor can be used to construct a non-const object too. When that happens, that object will be initialized using dynamic initialization. In your case, bar is a non-const object. Hence it make sense that it is initialized using dynamic initialization.

Changing your code to:

struct Bar {
    constexpr Bar()
        : value(0) { }
    int value;
};

constexpr Bar bar;

should change initialization of bar to static initialization.

However, if bar is changed to a const object, you won't be be able to use

bar.value = 1; 

in Init(). I just wanted to point out how to change bar so it can be initialized during static initialization.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • So then the order in which `foo` and `bar` are instantiated is dictated by the order in which they're declared, right? Does that mean it's technically UB that `Foo()` causes a write to `bar.value` even though `bar` hasn't been initialized yet? – lubgr Apr 04 '19 at 18:45
  • @lubgr, very much so. – R Sahu Apr 04 '19 at 18:51
  • From [basic.static.start]/2: _"A constant initializer for a variable or temporary object o is an initializer whose full-expression is a constant expression, except that if o is an object, such an initializer may also invoke constexpr constructors for o and its subobjects even if those objects are of non-literal class types ... Constant initialization is performed if a variable or temporary object with static or thread storage duration is initialized by a constant initializer for the entity"_. What am I missing? – undermind Apr 04 '19 at 19:11
  • @undermind, which version of the language is that from? C++14, C++17? – R Sahu Apr 04 '19 at 19:31
  • It's from C++17 working draft. In C++14 working draft we have another wording, but, imho, with the similar meaning: _A constant initializer for an object o is an expression that is a constant expression, except that it may also invoke constexpr constructors for o and its subobjects even if those objects are of non-literal class types... Constant initialization is performed: ... if an object with static or thread storage duration is initialized by a constructor call, and if the initialization full-expression is a constant initializer for the object_ – undermind Apr 04 '19 at 19:38
  • @undermind, thanks. I am not a language lawyer. I will have to shift through the details to make sense of it in non-language-lawyer speak :) – R Sahu Apr 04 '19 at 19:41
  • Me too :) But from non-language-lawyer point of view: what prevents compiler to initialize `bar` at compile time, if it's ctor is `constexpr`? – undermind Apr 04 '19 at 19:43
  • @undermind, it's not clear to me whether the compiler is required to do so or is that an option for the compiler. – R Sahu Apr 04 '19 at 19:44
  • Ok, yes, it may be just an option. But anyway, what is the difference between `const Bar` and `Bar`, that makes it possible to perform constant initialization in the const-case but not in non-const? – undermind Apr 04 '19 at 19:49
  • @undermind, now that I think about it, that might not be sufficient. `constexpr Bar bar;` will probably force "constant initialization" – R Sahu Apr 04 '19 at 19:52