Why "int const B::bsm1a;" not considered a redefinition? - c++

#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):
Why is this NOT considered a redefinition?
What does this line actually do here?

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.

Related

Undefined reference to static const member when compiled with -O0 [duplicate]

I have a class for local use only (i.e., its cope is only the c++ file it is defined in)
class A {
public:
static const int MY_CONST = 5;
};
void fun( int b ) {
int j = A::MY_CONST; // no problem
int k = std::min<int>( A::MY_CONST, b ); // link error:
// undefined reference to `A::MY_CONST`
}
All the code reside in the same c++ file. When compiling using VS on windows, there is no problem at all.
However, when compiling on Linux I get the undefined reference error only for the second statement.
Any suggestions?
std::min<int>'s arguments are both const int&(not just int), i.e. references to int. And you can't pass a reference to A::MY_CONST because it is not defined (only declared).
Provide a definition in the .cpp file, outside the class:
class A {
public:
static const int MY_CONST = 5; // declaration
};
const int A::MY_CONST; // definition (no value needed)
// initialize static constants outside the class
class A {
public:
static const int MY_CONST;
};
const int A::MY_CONST = 5;
void fun( int b ) {
int j = A::MY_CONST; // no problem
int k = std::min<int>( A::MY_CONST, b ); // link error:
// undefined reference to `A::MY_CONST`
}
To explain what's happening here:
You declared static const integer inside class, this "feature" is here to be able to use it as constant expression,i.e. for local array size, template non-type parameters, etc.. If compiler wants to use this constant expression it must be able to see it's value in that translation unit.
9.5/3
If a non-volatile 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 (5.19). A static data member of literal type can be declared in the
class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer
in which every initializer-clause that is an assignment-expression is a constant expression. [ Note: In both
these cases, the member may appear in constant expressions. — end note ] The member shall still be defined
in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition shall not
contain an initializer.
odr-used means to form reference to that variable or take it's address.
std::min takes it's parameters by reference, so they are odr-used.
Solution:
Define it!
class A
{
static const int a = 5;
};
const int A::a; //definition, shall not contain initializer
You can also save the const value to a local variable.
class A {
public:
static const int MY_CONST = 5;
};
void fun( int b ) {
int j = A::MY_CONST; // no problem
int k = std::min<int>( A::MY_CONST, b ); // link error: undefined reference to `A::MY_CONST`
int l = std::min<int>( j, b); // works
}
I am having a very strange situation
template<class T> class Strange {
public:
static const char gapchar='-';
};
template<class T> void Strange<T> method1 {
char tmp = gapchar;
}
template<class T> void Strange<T> method2 {
char tmp = gapchar;
}
I include the above class, it has been working for several years.
I added another method, essentially the same signature and just reading the gapchar.
I got undefined error only for the third method, even I am using all three methods.
Then I changed the way I initialize the static variable by
not initializing in the class definition:
static const char gapchar;
template<class T> const char Strange<T>::gapchar='-';
This solved the problem. I could not figure out why the old way of
initializing int or char type (the only two types allowed) inside the class
definition section stop working for only one of the methods but not others.
If you are using some header-only thing and want to avoid having to add a .cpp file, it seems like you can do this:
class A {
public:
static inline const int MY_CONST = 5;
};
(or `static inline constexpr`)
This requires C++17

C++: const for class members [duplicate]

#include <iostream>
using namespace std;
class T1
{
const int t = 100;
public:
T1()
{
cout << "T1 constructor: " << t << endl;
}
};
When I am trying to initialize the const member variable t with 100. But it's giving me the following error:
test.cpp:21: error: ISO C++ forbids initialization of member ‘t’
test.cpp:21: error: making ‘t’ static
How can I initialize a const value?
The const variable specifies whether a variable is modifiable or not. The constant value assigned will be used each time the variable is referenced. The value assigned cannot be modified during program execution.
Bjarne Stroustrup's explanation sums it up briefly:
A class is typically declared in a header file and a header file is typically included into many translation units. However, to avoid complicated linker rules, C++ requires that every object has a unique definition. That rule would be broken if C++ allowed in-class definition of entities that needed to be stored in memory as objects.
A const variable has to be declared within the class, but it cannot be defined in it. We need to define the const variable outside the class.
T1() : t( 100 ){}
Here the assignment t = 100 happens in initializer list, much before the class initilization occurs.
Well, you could make it static:
static const int t = 100;
or you could use a member initializer:
T1() : t(100)
{
// Other constructor stuff here
}
There are couple of ways to initialize the const members inside the class..
Definition of const member in general, needs initialization of the variable too..
1) Inside the class , if you want to initialize the const the syntax is like this
static const int a = 10; //at declaration
2) Second way can be
class A
{
static const int a; //declaration
};
const int A::a = 10; //defining the static member outside the class
3) Well if you don't want to initialize at declaration, then the other way is to through constructor, the variable needs to be initialized in the initialization list(not in the body of the constructor). It has to be like this
class A
{
const int b;
A(int c) : b(c) {} //const member initialized in initialization list
};
If you don't want to make the const data member in class static, You can initialize the const data member using the constructor of the class.
For example:
class Example{
const int x;
public:
Example(int n);
};
Example::Example(int n):x(n){
}
if there are multiple const data members in class you can use the following syntax to initialize the members:
Example::Example(int n, int z):x(n),someOtherConstVariable(z){}
You can upgrade your compiler to support C++11 and your code would work perfectly.
Use initialization list in constructor.
T1() : t( 100 )
{
}
Another solution is
class T1
{
enum
{
t = 100
};
public:
T1();
};
So t is initialised to 100 and it cannot be changed and it is private.
If a member is a Array it will be a little bit complex than the normal is:
class C
{
static const int ARRAY[10];
public:
C() {}
};
const unsigned int C::ARRAY[10] = {0,1,2,3,4,5,6,7,8,9};
or
int* a = new int[N];
// fill a
class C {
const std::vector<int> v;
public:
C():v(a, a+N) {}
};
Another possible way are namespaces:
#include <iostream>
namespace mySpace {
static const int T = 100;
}
using namespace std;
class T1
{
public:
T1()
{
cout << "T1 constructor: " << mySpace::T << endl;
}
};
The disadvantage is that other classes can also use the constants if they include the header file.
This is the right way to do. You can try this code.
#include <iostream>
using namespace std;
class T1 {
const int t;
public:
T1():t(100) {
cout << "T1 constructor: " << t << endl;
}
};
int main() {
T1 obj;
return 0;
}
if you are using C++10 Compiler or below then you can not initialize the cons member at the time of declaration. So here it is must to make constructor to initialise the const data member. It is also must to use initialiser list T1():t(100) to get memory at instant.
you can add static to make possible the initialization of this class member variable.
static const int i = 100;
However, this is not always a good practice to use inside class declaration, because all objects instacied from that class will shares the same static variable which is stored in internal memory outside of the scope memory of instantiated objects.
In C++ you cannot initialize any variables directly while the declaration.
For this we've to use the concept of constructors.
See this example:-
#include <iostream>
using namespace std;
class A
{
public:
const int x;
A():x(0) //initializing the value of x to 0
{
//constructor
}
};
int main()
{
A a; //creating object
cout << "Value of x:- " <<a.x<<endl;
return 0;
}
Hope it would help you!

Assignment is treated as initialization [duplicate]

#include <iostream>
using namespace std;
class T1
{
const int t = 100;
public:
T1()
{
cout << "T1 constructor: " << t << endl;
}
};
When I am trying to initialize the const member variable t with 100. But it's giving me the following error:
test.cpp:21: error: ISO C++ forbids initialization of member ‘t’
test.cpp:21: error: making ‘t’ static
How can I initialize a const value?
The const variable specifies whether a variable is modifiable or not. The constant value assigned will be used each time the variable is referenced. The value assigned cannot be modified during program execution.
Bjarne Stroustrup's explanation sums it up briefly:
A class is typically declared in a header file and a header file is typically included into many translation units. However, to avoid complicated linker rules, C++ requires that every object has a unique definition. That rule would be broken if C++ allowed in-class definition of entities that needed to be stored in memory as objects.
A const variable has to be declared within the class, but it cannot be defined in it. We need to define the const variable outside the class.
T1() : t( 100 ){}
Here the assignment t = 100 happens in initializer list, much before the class initilization occurs.
Well, you could make it static:
static const int t = 100;
or you could use a member initializer:
T1() : t(100)
{
// Other constructor stuff here
}
There are couple of ways to initialize the const members inside the class..
Definition of const member in general, needs initialization of the variable too..
1) Inside the class , if you want to initialize the const the syntax is like this
static const int a = 10; //at declaration
2) Second way can be
class A
{
static const int a; //declaration
};
const int A::a = 10; //defining the static member outside the class
3) Well if you don't want to initialize at declaration, then the other way is to through constructor, the variable needs to be initialized in the initialization list(not in the body of the constructor). It has to be like this
class A
{
const int b;
A(int c) : b(c) {} //const member initialized in initialization list
};
If you don't want to make the const data member in class static, You can initialize the const data member using the constructor of the class.
For example:
class Example{
const int x;
public:
Example(int n);
};
Example::Example(int n):x(n){
}
if there are multiple const data members in class you can use the following syntax to initialize the members:
Example::Example(int n, int z):x(n),someOtherConstVariable(z){}
You can upgrade your compiler to support C++11 and your code would work perfectly.
Use initialization list in constructor.
T1() : t( 100 )
{
}
Another solution is
class T1
{
enum
{
t = 100
};
public:
T1();
};
So t is initialised to 100 and it cannot be changed and it is private.
If a member is a Array it will be a little bit complex than the normal is:
class C
{
static const int ARRAY[10];
public:
C() {}
};
const unsigned int C::ARRAY[10] = {0,1,2,3,4,5,6,7,8,9};
or
int* a = new int[N];
// fill a
class C {
const std::vector<int> v;
public:
C():v(a, a+N) {}
};
Another possible way are namespaces:
#include <iostream>
namespace mySpace {
static const int T = 100;
}
using namespace std;
class T1
{
public:
T1()
{
cout << "T1 constructor: " << mySpace::T << endl;
}
};
The disadvantage is that other classes can also use the constants if they include the header file.
This is the right way to do. You can try this code.
#include <iostream>
using namespace std;
class T1 {
const int t;
public:
T1():t(100) {
cout << "T1 constructor: " << t << endl;
}
};
int main() {
T1 obj;
return 0;
}
if you are using C++10 Compiler or below then you can not initialize the cons member at the time of declaration. So here it is must to make constructor to initialise the const data member. It is also must to use initialiser list T1():t(100) to get memory at instant.
you can add static to make possible the initialization of this class member variable.
static const int i = 100;
However, this is not always a good practice to use inside class declaration, because all objects instacied from that class will shares the same static variable which is stored in internal memory outside of the scope memory of instantiated objects.
In C++ you cannot initialize any variables directly while the declaration.
For this we've to use the concept of constructors.
See this example:-
#include <iostream>
using namespace std;
class A
{
public:
const int x;
A():x(0) //initializing the value of x to 0
{
//constructor
}
};
int main()
{
A a; //creating object
cout << "Value of x:- " <<a.x<<endl;
return 0;
}
Hope it would help you!

static const member of a struct is not defined

Using an 03 standard-compliant compiler (safety-critical variant of gcc-3.3.2).
The standard says that static member objects must be defined (9.4.2 (4)). It also states that the one-definition rule holds, but no diagnostic is required (9.4.2 (5)). Is the following code valid?
struct fred
{
static const int JOE=1;
int m_joe;
fred() : m_joe(JOE) {}
};
That is, there is no "static const int fred::JOE;".
I ask because we have a case (apparently) where a static const int in a template class was never defined, and the code worked in some contexts, but not others. I replaced the static const int with an enum, and it worked in all cases.
Were we definitely in the Land of Undefined Behavior?
A static const int defines a compile-time constant; I'm afraid I can't refer to a specific part of the standard. The only time you need a definition for it is if you try to take the address of it or create a reference. If you use an enum instead, the compiler will create a temporary variable for you when you need a reference.
struct test
{
static const int one = 1;
enum { two = 2 };
};
void printint(const int & i)
{
cout << i << endl;
}
int main() {
printint(test::one); // error
printint(test::two); // no error
return 0;
}

static const in c++ class: undefined reference

I have a class for local use only (i.e., its cope is only the c++ file it is defined in)
class A {
public:
static const int MY_CONST = 5;
};
void fun( int b ) {
int j = A::MY_CONST; // no problem
int k = std::min<int>( A::MY_CONST, b ); // link error:
// undefined reference to `A::MY_CONST`
}
All the code reside in the same c++ file. When compiling using VS on windows, there is no problem at all.
However, when compiling on Linux I get the undefined reference error only for the second statement.
Any suggestions?
std::min<int>'s arguments are both const int&(not just int), i.e. references to int. And you can't pass a reference to A::MY_CONST because it is not defined (only declared).
Provide a definition in the .cpp file, outside the class:
class A {
public:
static const int MY_CONST = 5; // declaration
};
const int A::MY_CONST; // definition (no value needed)
// initialize static constants outside the class
class A {
public:
static const int MY_CONST;
};
const int A::MY_CONST = 5;
void fun( int b ) {
int j = A::MY_CONST; // no problem
int k = std::min<int>( A::MY_CONST, b ); // link error:
// undefined reference to `A::MY_CONST`
}
To explain what's happening here:
You declared static const integer inside class, this "feature" is here to be able to use it as constant expression,i.e. for local array size, template non-type parameters, etc.. If compiler wants to use this constant expression it must be able to see it's value in that translation unit.
9.5/3
If a non-volatile 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 (5.19). A static data member of literal type can be declared in the
class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer
in which every initializer-clause that is an assignment-expression is a constant expression. [ Note: In both
these cases, the member may appear in constant expressions. — end note ] The member shall still be defined
in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition shall not
contain an initializer.
odr-used means to form reference to that variable or take it's address.
std::min takes it's parameters by reference, so they are odr-used.
Solution:
Define it!
class A
{
static const int a = 5;
};
const int A::a; //definition, shall not contain initializer
You can also save the const value to a local variable.
class A {
public:
static const int MY_CONST = 5;
};
void fun( int b ) {
int j = A::MY_CONST; // no problem
int k = std::min<int>( A::MY_CONST, b ); // link error: undefined reference to `A::MY_CONST`
int l = std::min<int>( j, b); // works
}
I am having a very strange situation
template<class T> class Strange {
public:
static const char gapchar='-';
};
template<class T> void Strange<T> method1 {
char tmp = gapchar;
}
template<class T> void Strange<T> method2 {
char tmp = gapchar;
}
I include the above class, it has been working for several years.
I added another method, essentially the same signature and just reading the gapchar.
I got undefined error only for the third method, even I am using all three methods.
Then I changed the way I initialize the static variable by
not initializing in the class definition:
static const char gapchar;
template<class T> const char Strange<T>::gapchar='-';
This solved the problem. I could not figure out why the old way of
initializing int or char type (the only two types allowed) inside the class
definition section stop working for only one of the methods but not others.
If you are using some header-only thing and want to avoid having to add a .cpp file, it seems like you can do this:
class A {
public:
static inline const int MY_CONST = 5;
};
(or `static inline constexpr`)
This requires C++17