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?
Simple trick: use
+
before thekConst
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.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
tells us that the linker cannot find a symbol.
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:
Then, you can see that the compiler will put the definition in the generated object file:
Now, you can see the 'R' saying that it is defined in the data section.
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:
You can also replace it by a constexpr member function:
If you're writing static const variable with initializer inside class declaration it's just like as if you've written
and GCC will treat it the same way, meaning that it does not have an address.
The correct code should be
It's intentional, 9.4.2/4 says:
When you pass the static data member by const reference, you "use" it, 3.2/2:
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 callfoo(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 ;-)