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.