static const member of a struct is not defined - c++

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;
}

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

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

#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.

Any way to move constexpr function that returns std::optional inside struct where it is used? [duplicate]

I am using g++4.8.0, which doesn't contain earlier constexpr bug. Thus below code works fine:
constexpr int size() { return 5; }
int array[size()];
int main () {}
However, if I enclose both the variable inside a class as static, then it gives compiler error:
struct X {
constexpr static int size() { return 5; }
static const int array[size()];
};
int main () {}
Here is the error:
error: size of array ‘array’ is not an integral constant-expression
Is it forbidden to use constexpr in such a way or yet another g++ bug?
Yes, it is ill-formed. Here's why:
A constexpr function needs to be defined (not just declared) before being used in a constant expression.
So for example:
constexpr int f(); // declare f
constexpr int x = f(); // use f - ILLEGAL, f not defined
constexpr int f() { return 5; } // define f, too late
function definitions inside a class specifier (as well as initializers and default parameters) are essentially parsed in an order like they were defined outside the class.
So this:
struct X {
constexpr static int size() { return 5; }
static const int array[size()];
};
Is parsed in this order:
struct X {
constexpr inline static int size(); // function body defered
static const int array[size()]; // <--- POINT A
};
constexpr inline int X::size() { return 5; }
That is, parsing of function bodies are defered until after the class specifier.
The purpose of this deferral of function body parsing is so that function bodies can forward reference class members not yet declared at that point, and also so they can use their own class as a complete type:
struct X
{
void f() { T t; /* OK */ }
typedef int T;
};
Compared to at namespace scope:
void f() { T t; /* error, T not declared */ }
typedef int T;
At POINT A, the compiler doesn't have the definition of size() yet, so it can't call it. For compile-time performance constexpr functions need to be defined ahead of their use in the translation unit before being called during compile, otherwise the compiler would have to make a multiple passes just to "link" constant expressions for evaluation.
Apparently it's not even a bug, because its status is RESOLVED INVALID, which means that the people behind GCC and that bugzilla, after reviewing the problem, don't think that this is a GCC bug.
I remind you on that page because there is also an answer for this behaviour in one of the related posts.
I just wanted to add that even though this may not be good practice and will restrict you to defining the class body in the same compilation unit that its declared, it's possible to force the compiler to compile the definition of the function bodies at the same point as its declaration by adding a redundant template parameter:
template <typename = void>
struct X {
constexpr static int size() { return 5; }
static const int array[size()];
};
int main()
{
X<> x{};
...
}

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

making a constant array in c++

Is there any reason why codeblocks is telling me that I can't make an array? I'm simply trying to do:
const unsigned int ARRAY[10] = {0,1,2,3,4,5,6,7,8,9};
and it's giving me
error: a brace-enclosed initializer is not allowed here before '{' token
I have changed other parts of the initializer, but the error is always saying the same thing. This doesn't seem to make sense, since this is one of the first things I learned in c++.
You say that you did this within a class, as a private variable.
Recall that (at the moment), member variables may not be initialised in the same place where you declare them (with a few exceptions).
struct T {
std::string str = "lol";
};
is not ok. It has to be:
struct T {
std::string str;
T() : str("lol") {}
};
But, to add insult to injury, pre-C++0x you cannot initialise arrays in the ctor-initializer!:
struct T {
const unsigned int array[10];
T() : array({0,1,2,3,4,5,6,7,8,9}) {} // not possible :(
};
And, because your array's elements are const, you can't rely on assignment either:
struct T {
const unsigned int array[10];
T() {
for (int i = 0; i < 10; i++)
array[i] = i; // not possible :(
}
};
However, as some other contributors have quite rightly pointed out, there seems little point in having a copy of the array for each instance of T if you can't modify its elements. Instead, you could use a static member.
So, the following will ultimately solve your problem in what's — probably — the best way:
struct T {
static const unsigned int array[10];
};
const unsigned int T::array[10] = {0,1,2,3,4,5,6,7,8,9};
Hope this helps.
Since this is a private member variable in a class (according to the comment), this is indeed not allowed in C++03.
C++0x, partially supported by many modern compilers, allows the following to compile:
class C
{
const unsigned int ARRAY[10];
public:
C() : ARRAY{0,1,2,3,4,5,6,7,8,9} {}
};
int main()
{
C obj; // contains a non-static const member: non-assignable
}
However, non-static const members only make sense if they contain different values in different instances of the class. If every instance is to contain the same {0,1,2,3,4,5,6,7,8,9}, then you should make it static, which also makes it possible to do this in C++98:
class C
{
static const unsigned int ARRAY[10];
public:
C() {}
};
const unsigned int C::ARRAY[10] = {0,1,2,3,4,5,6,7,8,9};
int main()
{
C obj;
}