trouble with static const integral member declaration [duplicate] - c++

I ran into an interesting issue today. Consider this simple example:
template <typename T>
void foo(const T & a) { /* code */ }
// This would also fail
// void foo(const int & a) { /* code */ }
class Bar
{
public:
static const int kConst = 1;
void func()
{
foo(kConst); // This is the important line
}
};
int main()
{
Bar b;
b.func();
}
When compiling I get an error:
Undefined reference to 'Bar::kConst'
Now, I'm pretty sure that this is because the static const int is not defined anywhere, which is intentional because according to my understanding the compiler should be able to make the replacement at compile-time and not need a definition. However, since the function takes a const int & parameter, it seems to be not making the substitution, and instead preferring a reference. I can resolve this issue by making the following change:
foo(static_cast<int>(kConst));
I believe this is now forcing the compiler to make a temporary int, and then pass a reference to that, which it can successfully do at compile time.
I was wondering if this was intentional, or am I expecting too much from gcc to be able to handle this case? Or is this something I shouldn't be doing for some reason?

It's intentional, 9.4.2/4 says:
If a static data member is of const integral or const enumeration type,
its declaration in the class
definition can specify a
constant-initializer which shall be an
integral constant expression (5.19) In
that case, the member can appear in
integral constant expressions. The
member shall still be defined in a
namespace scope if it is used in the
program
When you pass the static data member by const reference, you "use" it, 3.2/2:
An expression is potentially evaluated
unless it appears where an integral
constant expression is required (see
5.19), is the operand of the sizeof operator (5.3.3), or is the operand of
the typeid operator and the expression
does not designate an lvalue of
polymorphic class type (5.2.8). An
object or non-overloaded function is
used if its name appears in a
potentially-evaluated expression.
So in fact, you "use" it when you pass it by value too, or in a static_cast. It's just that GCC has let you off the hook in one case but not the other.
[Edit: gcc is applying the rules from C++0x drafts: "A variable or non-overloaded function whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) is immediately applied.". The static cast performs lvalue-rvalue conversion immediately, so in C++0x it's not "used".]
The practical problem with the const reference is that foo is within its rights to take the address of its argument, and compare it for example with the address of the argument from another call, stored in a global. Since a static data member is a unique object, this means if you call foo(kConst) from two different TUs, then the address of the object passed must be the same in each case. AFAIK GCC can't arrange that unless the object is defined in one (and only one) TU.
OK, so in this case foo is a template, hence the definition is visible in all TUs, so perhaps the compiler could in theory rule out the risk that it does anything with the address. But in general you certainly shouldn't be taking addresses of or references to non-existent objects ;-)

If you're writing static const variable with initializer inside class declaration it's just like as if you've written
class Bar
{
enum { kConst = 1 };
}
and GCC will treat it the same way, meaning that it does not have an address.
The correct code should be
class Bar
{
static const int kConst;
}
const int Bar::kConst = 1;

This is a really valid case. Especially because foo could be a function from the STL like std::count which takes a const T& as its third argument.
I spent much time trying to understand why the linker had problems with such a basic code.
The error message
Undefined reference to 'Bar::kConst'
tells us that the linker cannot find a symbol.
$nm -C main.o
0000000000000000 T main
0000000000000000 W void foo<int>(int const&)
0000000000000000 W Bar::func()
0000000000000000 U Bar::kConst
We can see from the 'U' that Bar::kConst is undefined. Hence, when the linker tries to do its job, it has to find the symbol. But you only declare kConst and don't define it.
The solution in C++ is also to define it as follows:
template <typename T>
void foo(const T & a) { /* code */ }
class Bar
{
public:
static const int kConst = 1;
void func()
{
foo(kConst); // This is the important line
}
};
const int Bar::kConst; // Definition <--FIX
int main()
{
Bar b;
b.func();
}
Then, you can see that the compiler will put the definition in the generated object file:
$nm -C main.o
0000000000000000 T main
0000000000000000 W void foo<int>(int const&)
0000000000000000 W Bar::func()
0000000000000000 R Bar::kConst
Now, you can see the 'R' saying that it is defined in the data section.

The proper way in C++17 would be to simply make the variable constexpr:
static constexpr int kConst = 1;
constexpr static data member are implicitly inline.

You can also replace it by a constexpr member function:
class Bar
{
static constexpr int kConst() { return 1; };
};

g++ version 4.3.4 accepts this code (see this link). But g++ version 4.4.0 rejects it.

I think this artefact of C++ means that any time that Bar::kConst is referred to, its literal value is used instead.
This means that in practise there is no variable to make a reference point to.
You may have to do this:
void func()
{
int k = kConst;
foo(k);
}

Simple trick: use + before the kConst passed down the function. This will prevent the constant from being taken a reference from, and this way the code will not generate a linker request to the constant object, but it will go on with the compiler-time constant value instead.

I experienced the same problem as mentioned by Cloderic (static const in a ternary operator: r = s ? kConst1 : kConst2), but it only complained after turning off compiler optimization (-O0 instead of -Os). Happened on gcc-none-eabi 4.8.5.

Related

How do I resolve undefined reference errors for template specializations in header? [duplicate]

I ran into an interesting issue today. Consider this simple example:
template <typename T>
void foo(const T & a) { /* code */ }
// This would also fail
// void foo(const int & a) { /* code */ }
class Bar
{
public:
static const int kConst = 1;
void func()
{
foo(kConst); // This is the important line
}
};
int main()
{
Bar b;
b.func();
}
When compiling I get an error:
Undefined reference to 'Bar::kConst'
Now, I'm pretty sure that this is because the static const int is not defined anywhere, which is intentional because according to my understanding the compiler should be able to make the replacement at compile-time and not need a definition. However, since the function takes a const int & parameter, it seems to be not making the substitution, and instead preferring a reference. I can resolve this issue by making the following change:
foo(static_cast<int>(kConst));
I believe this is now forcing the compiler to make a temporary int, and then pass a reference to that, which it can successfully do at compile time.
I was wondering if this was intentional, or am I expecting too much from gcc to be able to handle this case? Or is this something I shouldn't be doing for some reason?
It's intentional, 9.4.2/4 says:
If a static data member is of const integral or const enumeration type,
its declaration in the class
definition can specify a
constant-initializer which shall be an
integral constant expression (5.19) In
that case, the member can appear in
integral constant expressions. The
member shall still be defined in a
namespace scope if it is used in the
program
When you pass the static data member by const reference, you "use" it, 3.2/2:
An expression is potentially evaluated
unless it appears where an integral
constant expression is required (see
5.19), is the operand of the sizeof operator (5.3.3), or is the operand of
the typeid operator and the expression
does not designate an lvalue of
polymorphic class type (5.2.8). An
object or non-overloaded function is
used if its name appears in a
potentially-evaluated expression.
So in fact, you "use" it when you pass it by value too, or in a static_cast. It's just that GCC has let you off the hook in one case but not the other.
[Edit: gcc is applying the rules from C++0x drafts: "A variable or non-overloaded function whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) is immediately applied.". The static cast performs lvalue-rvalue conversion immediately, so in C++0x it's not "used".]
The practical problem with the const reference is that foo is within its rights to take the address of its argument, and compare it for example with the address of the argument from another call, stored in a global. Since a static data member is a unique object, this means if you call foo(kConst) from two different TUs, then the address of the object passed must be the same in each case. AFAIK GCC can't arrange that unless the object is defined in one (and only one) TU.
OK, so in this case foo is a template, hence the definition is visible in all TUs, so perhaps the compiler could in theory rule out the risk that it does anything with the address. But in general you certainly shouldn't be taking addresses of or references to non-existent objects ;-)
If you're writing static const variable with initializer inside class declaration it's just like as if you've written
class Bar
{
enum { kConst = 1 };
}
and GCC will treat it the same way, meaning that it does not have an address.
The correct code should be
class Bar
{
static const int kConst;
}
const int Bar::kConst = 1;
This is a really valid case. Especially because foo could be a function from the STL like std::count which takes a const T& as its third argument.
I spent much time trying to understand why the linker had problems with such a basic code.
The error message
Undefined reference to 'Bar::kConst'
tells us that the linker cannot find a symbol.
$nm -C main.o
0000000000000000 T main
0000000000000000 W void foo<int>(int const&)
0000000000000000 W Bar::func()
0000000000000000 U Bar::kConst
We can see from the 'U' that Bar::kConst is undefined. Hence, when the linker tries to do its job, it has to find the symbol. But you only declare kConst and don't define it.
The solution in C++ is also to define it as follows:
template <typename T>
void foo(const T & a) { /* code */ }
class Bar
{
public:
static const int kConst = 1;
void func()
{
foo(kConst); // This is the important line
}
};
const int Bar::kConst; // Definition <--FIX
int main()
{
Bar b;
b.func();
}
Then, you can see that the compiler will put the definition in the generated object file:
$nm -C main.o
0000000000000000 T main
0000000000000000 W void foo<int>(int const&)
0000000000000000 W Bar::func()
0000000000000000 R Bar::kConst
Now, you can see the 'R' saying that it is defined in the data section.
The proper way in C++17 would be to simply make the variable constexpr:
static constexpr int kConst = 1;
constexpr static data member are implicitly inline.
You can also replace it by a constexpr member function:
class Bar
{
static constexpr int kConst() { return 1; };
};
g++ version 4.3.4 accepts this code (see this link). But g++ version 4.4.0 rejects it.
I think this artefact of C++ means that any time that Bar::kConst is referred to, its literal value is used instead.
This means that in practise there is no variable to make a reference point to.
You may have to do this:
void func()
{
int k = kConst;
foo(k);
}
Simple trick: use + before the kConst passed down the function. This will prevent the constant from being taken a reference from, and this way the code will not generate a linker request to the constant object, but it will go on with the compiler-time constant value instead.
I experienced the same problem as mentioned by Cloderic (static const in a ternary operator: r = s ? kConst1 : kConst2), but it only complained after turning off compiler optimization (-O0 instead of -Os). Happened on gcc-none-eabi 4.8.5.

MSVC compiler fatal error C1001 using pointer to method

While writing a custom reflection library I encountered a strange compiler behavior. However I was able to reproduce the problem with a much simplified code. Here is:
#include <iostream>
class OtherBase{};
class Base{};
/* Used only as a test class to verify if the reflection API works properly*/
class Derived : Base, OtherBase
{
public:
void Printer()
{
std::cout << "Derived::Printer() has been called" << std::endl;
}
};
/*Descriptor class that basically incapsulate the address of Derived::Printer method*/
struct ClassDescriptor
{
using type = Derived;
struct FuncDescriptor
{
static constexpr const auto member_address{ &type::Printer };
};
};
int main()
{
Derived derived;
auto address{ &Derived::Printer };
(derived.*address)(); // -> OK it compiles fine using the local variable address
(derived.*ClassDescriptor::FuncDescriptor::member_address)(); // -> BROKEN using the address from the descriptor class cause fatal error C1001 !
}
While trying debugging this problem I noticed that:
It happen only if Derived has multiple inheritance.
If I swap static constexpr const auto member_address{ &type::Printer } with inline static const auto member_address{ &type::Printer } it works.
Is it just a compiler bug, or I'm doing something wrong ?
Can I solve this problem while keeping the constexpr ?
Please note that I'm using MSVC 2017 with the compiler version 19.16.27024.1
All compiler options are default except for /std:c++17 enabled.
I know that updating (and surely i'll do it) the compiler version to the last one will probably solve the issue, but for now I would like to understand more about this problem.
About C1001, Microsoft Developer Network suggests that you remove some optimizations in your code: Fatal Error C1001. Once you've worked out which optimization is causing the issue, you can use a #pragma to disable that optimization in just that area:
// Disable the optimization
#pragma optimize( "", off )
...
// Re-enable any previous optimization
#pragma optimize( "", on )
Also, a fix for this issue has been released by Microsoft. You could install the most recent release.
const and constexpr:
const declares an object as constant. This implies a guarantee that once initialized, the value of that object won't change, and the compiler can make use of this fact for optimizations. It also helps prevent the programmer from writing code that modifies objects that were not meant to be modified after initialization.
constexpr declares an object as fit for use in what the Standard calls constant expressions. But note that constexpr is not the only way to do this.
When applied to functions the basic difference is this:
const can only be used for non-static member functions, not functions in general. It gives a guarantee that the member function does not modify any of the non-static data members.
constexpr can be used with both member and non-member functions, as well as constructors. It declares the function fit for use in constant expressions. The compiler will only accept it if the function meets certain criteria (7.1.5/3,4), most importantly:
The function body must be non-virtual and extremely simple: Apart from typedefs and static asserts, only a single return statement is allowed. In the case of a constructor, only an initialization list, typedefs, and static assert are allowed. (= default and = delete are allowed, too, though.)
As of C++14, the rules are more relaxed, what is allowed since then inside a constexpr function: asm declaration, a goto statement, a statement with a label other than case and default, try-block, the definition of a variable of non-literal type, definition of a variable of static or thread storage duration, the definition of a variable for which no initialization is performed.
The arguments and the return type must be literal types (i.e., generally speaking, very simple types, typically scalars or aggregates)
When can I / should I use both, const and constexpr together?
A. In object declarations. This is never necessary when both keywords refer to the same object to be declared. constexpr implies const.
constexpr const int N = 5;
is the same as
constexpr int N = 5;
However, note that there may be situations when the keywords each refer to different parts of the declaration:
static constexpr int N = 3;
int main()
{
constexpr const int *NP = &N;
}
Here, NP is declared as an address constant-expression, i.e. a pointer that is itself a constant expression. (This is possible when the address is generated by applying the address operator to a static/global constant expression.) Here, both constexpr and const are required: constexpr always refers to the expression being declared (here NP), while const refers to int (it declares a pointer-to-const). Removing the const would render the expression illegal (because (a) a pointer to a non-const object cannot be a constant expression, and (b) &N is in-fact a pointer-to-constant).
B. In member function declarations. In C++11, constexpr implies const, while in C++14 and C++17 that is not the case. A member function declared under C++11 as
constexpr void f();
needs to be declared as
constexpr void f() const;
under C++14 in order to still be usable as a const function.
You could refer to this link for more details.

Undefined reference error when initializing unique_ptr with a static const

When I try to use a static const to initialize a unique_ptr, I get an "undefined reference" error. However, when I new a pointer using the same constant, the symbol seems to be magically defined.
Here is a simple program that reproduces the error:
Outside_library.h
class Outside_library
{
public:
static const int my_const = 100;
};
main.cpp
#include "Outside_library.h"
#include <iostream>
#include <memory>
class My_class
{
public:
My_class(int num)
{
m_num = num;
};
virtual ~My_class(){};
private:
int m_num;
};
int main(int, char* [])
{
My_class* p_class = new My_class(Outside_library::my_const);
delete p_class;
// ==== ERROR HERE: When I comment this line out, the program runs fine.
std::unique_ptr<My_class> p_unique
= std::make_unique<My_class>(Outside_library::my_const);
std::cout << "I made it through!" << std::endl;
return 0;
}
I compiled the program using
g++ main.cpp -std=c++14
and got the following error.
/tmp/ccpJSQJS.o: In function `main':
main.cpp:(.text+0x51): undefined reference to `Outside_library::my_const'
collect2: error: ld returned 1 exit status
Can someone please help me understand why the constant is defined when using new, but not when using make_unique?
C++ has something known as the One-Definition Rule (ODR):
Informally, an object is odr-used if its value is read (unless it is a compile time constant) or written, its address is taken, or a reference is bound to it; a reference is odr-used if it is used and its referent is not known at compile time; and a function is odr-used if a function call to it is made or its address is taken. If an object, a reference or a function is odr-used, its definition must exist somewhere in the program; a violation of that is usually a link-time error.
The linked site gives the following example:
struct S {
static const int x = 0; // static data member
// a definition outside of class is required if it is odr-used
};
const int& f(const int& r);
int n = b ? (1, S::x) // S::x is not odr-used here
: f(S::x); // S::x is odr-used here: a definition is required
Your explicit constructor invocation does not "odr-use" Outside_library::my_const but the call to std::make_unique() does. When an object is odr-used it must have exactly one definition (not declaration). Your example has a declaration only. Again from cppreference:
a variable x in a potentially-evaluated expression ex is odr-used unless both of the following are true:
applying lvalue-to-rvalue conversion to x yields a constant expression that doesn't invoke non-trivial functions
either x is not an object (that is, x is a reference) or, if x is an object, it is one of the potential results of a larger expression e, where that larger expression is either a discarded-value expression or has the lvalue-to-rvalue conversion applied to it
The solution as suggested by Jarod42 is to use constexpr instead of const (if you have control over the "outside library" code). If you do not, then you'll need to link the program against the library that contains the definition of Outside_library::my_const.
g++ main.cpp -std=c++14 -lOutside_library
make_unique takes (forwardind) reference of its parameter, and so odr-use it.
My_class(Outside_library::my_const) only use the value.
One solution is to define the member in a TU:
const int Outside_library::my_const;
or using constexpr value (since C++11):
class Outside_library
{
public:
static constexpr int my_const = 100;
};

Undefined reference to static const member [duplicate]

I ran into an interesting issue today. Consider this simple example:
template <typename T>
void foo(const T & a) { /* code */ }
// This would also fail
// void foo(const int & a) { /* code */ }
class Bar
{
public:
static const int kConst = 1;
void func()
{
foo(kConst); // This is the important line
}
};
int main()
{
Bar b;
b.func();
}
When compiling I get an error:
Undefined reference to 'Bar::kConst'
Now, I'm pretty sure that this is because the static const int is not defined anywhere, which is intentional because according to my understanding the compiler should be able to make the replacement at compile-time and not need a definition. However, since the function takes a const int & parameter, it seems to be not making the substitution, and instead preferring a reference. I can resolve this issue by making the following change:
foo(static_cast<int>(kConst));
I believe this is now forcing the compiler to make a temporary int, and then pass a reference to that, which it can successfully do at compile time.
I was wondering if this was intentional, or am I expecting too much from gcc to be able to handle this case? Or is this something I shouldn't be doing for some reason?
It's intentional, 9.4.2/4 says:
If a static data member is of const integral or const enumeration type,
its declaration in the class
definition can specify a
constant-initializer which shall be an
integral constant expression (5.19) In
that case, the member can appear in
integral constant expressions. The
member shall still be defined in a
namespace scope if it is used in the
program
When you pass the static data member by const reference, you "use" it, 3.2/2:
An expression is potentially evaluated
unless it appears where an integral
constant expression is required (see
5.19), is the operand of the sizeof operator (5.3.3), or is the operand of
the typeid operator and the expression
does not designate an lvalue of
polymorphic class type (5.2.8). An
object or non-overloaded function is
used if its name appears in a
potentially-evaluated expression.
So in fact, you "use" it when you pass it by value too, or in a static_cast. It's just that GCC has let you off the hook in one case but not the other.
[Edit: gcc is applying the rules from C++0x drafts: "A variable or non-overloaded function whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) is immediately applied.". The static cast performs lvalue-rvalue conversion immediately, so in C++0x it's not "used".]
The practical problem with the const reference is that foo is within its rights to take the address of its argument, and compare it for example with the address of the argument from another call, stored in a global. Since a static data member is a unique object, this means if you call foo(kConst) from two different TUs, then the address of the object passed must be the same in each case. AFAIK GCC can't arrange that unless the object is defined in one (and only one) TU.
OK, so in this case foo is a template, hence the definition is visible in all TUs, so perhaps the compiler could in theory rule out the risk that it does anything with the address. But in general you certainly shouldn't be taking addresses of or references to non-existent objects ;-)
If you're writing static const variable with initializer inside class declaration it's just like as if you've written
class Bar
{
enum { kConst = 1 };
}
and GCC will treat it the same way, meaning that it does not have an address.
The correct code should be
class Bar
{
static const int kConst;
}
const int Bar::kConst = 1;
This is a really valid case. Especially because foo could be a function from the STL like std::count which takes a const T& as its third argument.
I spent much time trying to understand why the linker had problems with such a basic code.
The error message
Undefined reference to 'Bar::kConst'
tells us that the linker cannot find a symbol.
$nm -C main.o
0000000000000000 T main
0000000000000000 W void foo<int>(int const&)
0000000000000000 W Bar::func()
0000000000000000 U Bar::kConst
We can see from the 'U' that Bar::kConst is undefined. Hence, when the linker tries to do its job, it has to find the symbol. But you only declare kConst and don't define it.
The solution in C++ is also to define it as follows:
template <typename T>
void foo(const T & a) { /* code */ }
class Bar
{
public:
static const int kConst = 1;
void func()
{
foo(kConst); // This is the important line
}
};
const int Bar::kConst; // Definition <--FIX
int main()
{
Bar b;
b.func();
}
Then, you can see that the compiler will put the definition in the generated object file:
$nm -C main.o
0000000000000000 T main
0000000000000000 W void foo<int>(int const&)
0000000000000000 W Bar::func()
0000000000000000 R Bar::kConst
Now, you can see the 'R' saying that it is defined in the data section.
The proper way in C++17 would be to simply make the variable constexpr:
static constexpr int kConst = 1;
constexpr static data member are implicitly inline.
You can also replace it by a constexpr member function:
class Bar
{
static constexpr int kConst() { return 1; };
};
g++ version 4.3.4 accepts this code (see this link). But g++ version 4.4.0 rejects it.
I think this artefact of C++ means that any time that Bar::kConst is referred to, its literal value is used instead.
This means that in practise there is no variable to make a reference point to.
You may have to do this:
void func()
{
int k = kConst;
foo(k);
}
Simple trick: use + before the kConst passed down the function. This will prevent the constant from being taken a reference from, and this way the code will not generate a linker request to the constant object, but it will go on with the compiler-time constant value instead.
I experienced the same problem as mentioned by Cloderic (static const in a ternary operator: r = s ? kConst1 : kConst2), but it only complained after turning off compiler optimization (-O0 instead of -Os). Happened on gcc-none-eabi 4.8.5.

Undefined reference to static const int

I ran into an interesting issue today. Consider this simple example:
template <typename T>
void foo(const T & a) { /* code */ }
// This would also fail
// void foo(const int & a) { /* code */ }
class Bar
{
public:
static const int kConst = 1;
void func()
{
foo(kConst); // This is the important line
}
};
int main()
{
Bar b;
b.func();
}
When compiling I get an error:
Undefined reference to 'Bar::kConst'
Now, I'm pretty sure that this is because the static const int is not defined anywhere, which is intentional because according to my understanding the compiler should be able to make the replacement at compile-time and not need a definition. However, since the function takes a const int & parameter, it seems to be not making the substitution, and instead preferring a reference. I can resolve this issue by making the following change:
foo(static_cast<int>(kConst));
I believe this is now forcing the compiler to make a temporary int, and then pass a reference to that, which it can successfully do at compile time.
I was wondering if this was intentional, or am I expecting too much from gcc to be able to handle this case? Or is this something I shouldn't be doing for some reason?
It's intentional, 9.4.2/4 says:
If a static data member is of const integral or const enumeration type,
its declaration in the class
definition can specify a
constant-initializer which shall be an
integral constant expression (5.19) In
that case, the member can appear in
integral constant expressions. The
member shall still be defined in a
namespace scope if it is used in the
program
When you pass the static data member by const reference, you "use" it, 3.2/2:
An expression is potentially evaluated
unless it appears where an integral
constant expression is required (see
5.19), is the operand of the sizeof operator (5.3.3), or is the operand of
the typeid operator and the expression
does not designate an lvalue of
polymorphic class type (5.2.8). An
object or non-overloaded function is
used if its name appears in a
potentially-evaluated expression.
So in fact, you "use" it when you pass it by value too, or in a static_cast. It's just that GCC has let you off the hook in one case but not the other.
[Edit: gcc is applying the rules from C++0x drafts: "A variable or non-overloaded function whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) is immediately applied.". The static cast performs lvalue-rvalue conversion immediately, so in C++0x it's not "used".]
The practical problem with the const reference is that foo is within its rights to take the address of its argument, and compare it for example with the address of the argument from another call, stored in a global. Since a static data member is a unique object, this means if you call foo(kConst) from two different TUs, then the address of the object passed must be the same in each case. AFAIK GCC can't arrange that unless the object is defined in one (and only one) TU.
OK, so in this case foo is a template, hence the definition is visible in all TUs, so perhaps the compiler could in theory rule out the risk that it does anything with the address. But in general you certainly shouldn't be taking addresses of or references to non-existent objects ;-)
If you're writing static const variable with initializer inside class declaration it's just like as if you've written
class Bar
{
enum { kConst = 1 };
}
and GCC will treat it the same way, meaning that it does not have an address.
The correct code should be
class Bar
{
static const int kConst;
}
const int Bar::kConst = 1;
This is a really valid case. Especially because foo could be a function from the STL like std::count which takes a const T& as its third argument.
I spent much time trying to understand why the linker had problems with such a basic code.
The error message
Undefined reference to 'Bar::kConst'
tells us that the linker cannot find a symbol.
$nm -C main.o
0000000000000000 T main
0000000000000000 W void foo<int>(int const&)
0000000000000000 W Bar::func()
0000000000000000 U Bar::kConst
We can see from the 'U' that Bar::kConst is undefined. Hence, when the linker tries to do its job, it has to find the symbol. But you only declare kConst and don't define it.
The solution in C++ is also to define it as follows:
template <typename T>
void foo(const T & a) { /* code */ }
class Bar
{
public:
static const int kConst = 1;
void func()
{
foo(kConst); // This is the important line
}
};
const int Bar::kConst; // Definition <--FIX
int main()
{
Bar b;
b.func();
}
Then, you can see that the compiler will put the definition in the generated object file:
$nm -C main.o
0000000000000000 T main
0000000000000000 W void foo<int>(int const&)
0000000000000000 W Bar::func()
0000000000000000 R Bar::kConst
Now, you can see the 'R' saying that it is defined in the data section.
The proper way in C++17 would be to simply make the variable constexpr:
static constexpr int kConst = 1;
constexpr static data member are implicitly inline.
You can also replace it by a constexpr member function:
class Bar
{
static constexpr int kConst() { return 1; };
};
g++ version 4.3.4 accepts this code (see this link). But g++ version 4.4.0 rejects it.
I think this artefact of C++ means that any time that Bar::kConst is referred to, its literal value is used instead.
This means that in practise there is no variable to make a reference point to.
You may have to do this:
void func()
{
int k = kConst;
foo(k);
}
Simple trick: use + before the kConst passed down the function. This will prevent the constant from being taken a reference from, and this way the code will not generate a linker request to the constant object, but it will go on with the compiler-time constant value instead.
I experienced the same problem as mentioned by Cloderic (static const in a ternary operator: r = s ? kConst1 : kConst2), but it only complained after turning off compiler optimization (-O0 instead of -Os). Happened on gcc-none-eabi 4.8.5.