1

So, I've been experimenting with static class fields (especially the constant ones), and got myself into.... this:

#include <iostream>
#include <conio.h>

class Test {
public:
    Test() { std::cout << "Constructing (Default CTOR)\n"; }
    Test(int f) { std::cout << "Constructing (Int arg CTOR)\n"; }

    void method() const { std::cout << "Already constructed and being used\n"; }
};

class Stack {
public:
    // static const Test what{ 5 }; // - "element of type "const Test" can not have an initializer inside of a class"
    // const Test ok{ 5 }; // now it can (?)

    static const Test what;

    Stack() {
        what.method();
    }

    // Stack() : what{5} {} // can't do that because "what" will be dependent on object creation (which is not how static class fields roll)
};

Stack obj;

const Test Stack::what{};

int main()
{
    _getch();
    return 0;
}

Output:

Output

Apparently, static const Test what in Stack is being used before is has actually been created(?).

After that I ran another test:

#include <iostream>
#include <conio.h>

class Test {
public:
    int data;

    Test() { std::cout << "CONSTRUCTING (Default CTOR)\n"; } // notice, value-initialization of 'data' has been removed
    Test(int f) : data{ f } { std::cout << "CONSTRUCTING (Int arg CTOR)\n"; }

    void method() const { std::cout << "ALREADY CONSTRUCTED AND BEING USED :)\n" << data << std::endl; }
};

class Stack {
public:
    static const Test what;

    Stack() {
        what.method();
    }
};

Stack obj;

const Test Stack::what{ 5 };

int main()
{
    obj.what.method();

    _getch();
    return 0;
}

In this code I was hoping to see some sort of error, but the output ended up looking like this:

Output 2

I have some assumptions on what is happening here, but I'm not sure if they're correct. So, if they are, please correct me.

Here are my assumptions:

Basically, static variables are created at the very start of the program (and are value-initialized) and destoyed at the very end of the program (when you actually close your .exe). In my examples I have a static constant variable what in the class Stack and I think it is being created at the beginning of my program value-initialized. That's why its data field is set 0 and we can use its methods. But I don't think that's correct because it would've output Constructing (Default CTOR) into the console. So I'm kinda stuck there...

I also can not understand why the commented lines in my first example are illegal. What rules of static/constant class fields do they break exactly?

If you have any idea to what is happening in my examples please explain it.

Thanks for your attention.

Michael
  • 548
  • 6
  • 23
  • I suspect it's related to the fact that the default constructor doesn't actually modify anything in the object. What happens if you change the second default constructor to have `: data {1}` ? – Barmar Feb 09 '17 at 23:17
  • @Barmar, nothing. The output is still the same and there are no errors. – Michael Feb 09 '17 at 23:19
  • Because you `#include ` I cannot compile your code. If you had stuck to standard C++ I might have been able to help you out. – nwp Feb 09 '17 at 23:21
  • @nwp There's only one line that depends on conio.h, `_getch(();`. Surely you can figure out how to replace that with standard C++. – Barmar Feb 09 '17 at 23:23
  • @nwp Oh, that's just for me. To keep the console from closing. The code doesn't really depend on it, you can remove it or change it to `std::cin.get();` – Michael Feb 09 '17 at 23:24
  • 1
    I believe this is expected behavior. Constructors for static objects are called in the order they appear in the compilation unit; the order of construction for static objects in different compilation units is undefined. You are able to reference an unconstructed static object from code called from a static constructor. Unlike some languages, there is no "class constructor" that is called before static objects in the class are referenced. Now I invite people who actually read the spec to point out where I am wrong... – antlersoft Feb 09 '17 at 23:25

1 Answers1

1

Static variables at namespace scope (i.e. not inside a function) are constructed in the order of their definitions in the source file. This ordering only applies between variables in the same source file, not between different source files.

So obj is always constructed before what. There are limited things that can be done with objects that have a constructor but before the constructor has been called; and invoking a member function is not one of them (C++14 [basic.life]/5.2). So the call what.method() in Stack's constructor causes undefined behaviour.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • Ah, I see. Thanks for the informative answer! So the zero in my second exmaple's output should've been an error after all :) – Michael Feb 10 '17 at 03:38