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;
};
Related
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.
EDIT: I'm not asking how to make the code work, but why it has error. Also, the static member is not only declared inside the class, but also has been initialized with a value (at least it appears to have), hence I don't think it's redundant. Also, this question asks beyond that, so please don't close it. It is not a duplicate.
Say class A has a static member data defined inside. The shell record below does not link (ld: undefined reference).
Note that since C++11, std::vector<>::push_back() accepts either a const lvalue reference (const value_type&) or an rvalue reference (value_type&& val). Moreover, even if it doesn't accept an rvalue reference, an rvalue can be bound to a const lvalue reference.
$ cat class.h
struct A {
static const int STAT_MEMBER = 1;
A();
int a;
};
$ cat class.cc
#include "class.h"
#include <vector>
using namespace std;
A::A() { a = STAT_MEMBER; }
int main() {
vector<int> v;
v.push_back(A::STAT_MEMBER);
}
$ g++ -std=c++14 class.cc -o class
/tmp/ccNXDnyu.o: In function `main':
class.cc:(.text+0x2f): undefined reference to `A::STAT_MEMBER'
collect2: error: ld returned 1 exit status
$
Question:
(1) is A::STATIC_MEMBER an rvalue? (but regardless of it being an rvalue or an lvalue, it shouldn't cause an error in linker -- it should have been fine or have caused an error in compiler)
(2) why didn't the compiler/linker complain about STATIC_MEMBER in A::A()?
(3) why didn't the compiler complain about STATIC_MEMBER in main()?
(4) why did the linker complain about STATIC_MEMBER in main()?
I know this problem should be gone if the static member is declared inside the class but defined outside the class, but I'm mostly interested the problem above.
G++ version: g++-6 (Ubuntu/Linaro 6.3.0-18ubuntu2~14.04) 6.3.0 20170519
The reason is quite easy:
If you use a value, the compiler can use the known value to your undefined but declared static const member.
But the vector::push_back takes the reference to the value! The reference is the address and there is no address of an undefined object, only the value of the object is known.
You can reduce the code to this, which simplifies it a bit:
struct A {
static const int STAT_MEMBER = 1;
};
void Works( const int i ){} // this takes the VALUE of the variable
void Fail( const int& i){} // this takes the ADDRESS of the variable
// but there is no object which can be
// addressed, because it is not defined!
int main()
{
Works( A::STAT_MEMBER );
Fail( A::STAT_MEMBER );
}
Simply comment in and out to get linker error or not!
This only works in C++17, so just wait a little bit (or turn on the C++17 support flag in your compiler).
From the cppreference.com:
A static data member may be declared inline. An inline static data member can be defined in the class definition and may specify an initializer. It does not need an out-of-class definition:
struct X
{
inline static int n = 1;
};
This can be simplified to this:
struct A { static const int s = 1; };
int main()
{
int a1 = A::s; // OK
int const & a2 = A::s; // error,
int const * a3 = &A::s; // same thing
}
a1 case works because of the special treatment that integral constants receive. Basically compiler knows the value of s at compile time and does not need to access storage. This also allows s to be used as template parameters:
::std::array<int,A::s> xx;
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.
The sentence is part of paragraph §3.2/2 :
A variable 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.
What exactly does the sentence in bold mean above?
Edit:
The answer to the question of which this one is considered a duplicate, doesn't say anything that could answer my question.
It means that when you use a constant as a constant, it's just like you were actually using a constant.
struct S {
static const int i = 0;
};
int main() {
return S::i;
}
Although S::i has an initialiser, it has no definition, but the text in your question makes a special exception for uses like this, where S::i is only accessed for its value. In that case, no definition is needed.
On the other hand, other uses do require a definition:
struct S {
static const int i = 0;
};
int f(const int &i) {
return i;
}
int main() {
return f(S::i);
}
This program is invalid, and is accepted by some implementations, but rejected by others. The call to f requires an actual definition of S::i to exist, although if f gets inlined, it's possible for the lack of a definition to go undiagnosed.
On my system, if compiling and linking the second program without optimisations, I get:
$ g++ test2.cc -o test2
/tmp/ccdEsfxs.o:test2.cc:function main: error: undefined reference to 'S::i'
collect2: error: ld returned 1 exit status
To make it work, a definition needs to be provided, like so:
struct S {
static const int i = 0;
};
const int S::i;
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.