2
#include <iostream>
struct B {
public:
    static int const bsm1a{ 0xf };
};
int const B::bsm1a; // 1) Why is this NOT considered a redefinition?
                    // 2) What does this line actually do here?

int main()
{
    std::cout << "Hello World!\n";
}
#include <iostream>
struct B {
public:
    static int const bsm1a{ 0xf };
    static int const bsm1a; // Considered as a redefinition here and throws error C2086
};

int main()
{
    std::cout << "Hello World!\n";
}

My questions (embedded above in the code):

  1. Why is this NOT considered a redefinition?
  2. What does this line actually do here?
SAbboushi
  • 59
  • 6
  • 1) it's not a redefinition because the previous was not a definition; 2) it's a definition and provides the storage for `B::bsm1a`. – Eljay Oct 03 '20 at 15:43
  • If the error message for the second example says "redefinition" instead of "redeclaration", then your compiler is in error. – JaMiT Oct 03 '20 at 15:51
  • One way to determine what a line actually does is to remove it and see what changes. When someone else did that in a case similar to yours, the result was [Compilation failed, c++ program with static variable as private member variable](https://stackoverflow.com/questions/13125662/compilation-failed-c-program-with-static-variable-as-private-member-variable). – JaMiT Oct 03 '20 at 15:58
  • Thanks for your posts, but I still have the same questions: I've already spent hours researching this. Both of your responses are telling me that the latest Visual Studios version C++ compiler is mistaken. Removing the line gives no clue that I can determine. However, when the line is removed, the value of B::bsm1a is 0xf, so I'm confused by Eljay's response that it "provides the storage for B::bsm1a": it's not clear to me how it is stored any differently by having that line. – SAbboushi Oct 03 '20 at 19:23
  • btw - I had come across other posts saying that "Functions and variables declared inline may be defined multiple times in a program". I'm thinking that's related to my question, but I'm still confused hence my post. – SAbboushi Oct 03 '20 at 19:30
  • @SAbboushi Ah, you have it marked `const`. There is a special case for `static const` members that allows them to be used without an explicit definition (basically, every use of the value of `bsm1a` is replaced by `0xf` at compile time instead of being retrieved at run time). So, right, removing that line is not as instructive as I thought it would be. Would you be willing to remove the `const` and the definition to see what happens? – JaMiT Oct 04 '20 at 01:31

1 Answers1

7

This is what I gathered reading the C++ standard. If my interpretation is wrong, please correct me.

In the first case the situation is rather interesting. This comes down to the meaning of of declaration and definition in C++. If you dig into the C++ Standard, you'll find a section of this topic:

(§6.2) Each entity declared by a declaration is also defined by that declaration unless:

(§6.2.3) it declares a non-inline static data member in a class definition (11.4, 11.4.8)

This tells us that the line static int const bsm1a{ 0xf }; is not a definition but a declaration.

Defining static const data members inside a class used to disallowed because when the static member was part of a class definition and got included in multiple different translation units, it would violate the One Definition Rule (You can now do it post-C++17 using inline variables). So the way this used to be written was

struct Foo {
public:
    static const std::string str; //Declaration
};
const std::string Foo::str = "Hello World\n"; //Definition

However, it is allowed to declare a static const variable of integral or enumeration type inside a class definition per §11.4.8.2.4

If a non-volatile non-inline const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression

static int const bsm1a{ 0xf }; fits that description and thus it is allowed, but it is still a declaration.

At this point, B::bsm1a is declared but not defined. You can give it a value that's an integer or an enum because the compiler can put this into a register and never actually allocate memory for it.

#include <iostream>

struct B {
    static const int bsm1a{0xf}; // Compiler keeps track of the 0xf internally
};

int main()
{
    std::cout << &B::bsm1a << "\n"; //Whoops, this is not defined and there is no memory allocated to it, so taking its address is impossible (you'll get a linker error)
}

When you comment out the std::cout and examine the assembly, there is no reference to the B::bsm1a. When you later write int const B::bsm1a; this is not a declaration but a definition. Only at this point does the variable become defined and memory is allocated to it. When you examine the assembly of the code below, you will see that now "15" appears in the final assembly and call to &B::bsm1a actually addresses its memory location (movl $B::bsm1a, %esi)

#include <iostream>

struct B {
    static const int bsm1a{0xf}; 
};
const int B::bsm1a; // It is now defined and has memory assigned to it

int main()
{
    std::cout << &B::bsm1a << "\n"; // We can now take its address
}

In the second case you have another issue.

(§11.4.5) A member shall not be declared twice in the member-specification, except that

(§5.1) a nested class or member class template can be declared and then later defined, and (§5.2) an enumeration can be introduced with an opaque-enum-declaration and later redeclared with an enum-specifier.

You are simply not allowed to redeclare it within the class member-specification unless it meets those two criteria. So for example the following would be legal:

struct Foo
{
public:
    struct X; // declaration
    struct X; // redeclaration, allowed
};

But since your static const int is not one of the these types, this is illegal.

NotAProgrammer
  • 552
  • 1
  • 5
  • 15
  • >Only at this point does the variable become defined and memory is allocated to it. I'm not clear on why this statement results in memory being allocated. I've been researching linking, ODR-use, declaration vs. definition... bottom-line seems to be that definition results in memory allocation, but still unclear on what determines whether a statement is a definition or not? Seems maybe the only way to know is by what the documentation says? Will something like DumpBin (or some other way) point out whether an entity is defined or not? btw - AWESOMELY DOCUMENTED response – SAbboushi Oct 06 '20 at 00:50
  • If you read sections 6.2.1 and 6.2.2 ([basic.def]) it will show you which declarations are also definitions. Basically almost all declarations are also definitions, other than the exceptions outlined in 6.2.2. – NotAProgrammer Oct 06 '20 at 09:33
  • Thanks - I've just re-read them but am still stuck on how to confirm re: "2) What does this line actually do here?" If memory was allocated as I understand you and @Eljay are stating, wouldn't "&B::bsm1a" give me an address (Immediate Window, Main())? I find it will only give me an address if I instantiate on that line instead of in class... – SAbboushi Oct 06 '20 at 13:50
  • I edited my answer with more details, let me know if it's clearer now. – NotAProgrammer Oct 06 '20 at 14:13
  • Astoundingly clearer! especially with links to Compiler Explorer. – SAbboushi Oct 06 '20 at 17:38
  • I think part of confusion because I'm using MSVC compiler/debugger and posters are not. From my research: because B::bsm1a is not ODR-used, MSVC compiler resolves B::bsm1a directly without allocating memory. I've confirmed this by using MS dumpbin.exe to examine .obj file: memory is not allocated for B::bsm1a. This explains why "&B::bsm1a" in Immediate Window returns "expression must be an lvalue or a function designator". In contrast, gcc compiler allocates memory to the data member (see @NotAProgrammer 's links to Compiler Explorer). – SAbboushi Oct 07 '20 at 18:28
  • So I think lesson learned for me: just because I "define" an entity does NOT necessarily mean that memory is allocated... Thanks all for your help. – SAbboushi Oct 07 '20 at 18:29