Reputation: 10539
When I try to compile this,
#include <iostream>
struct K{
const static int a = 5;
};
int main(){
K k;
std::cout << std::min(k.a, 7);
}
I get following. Both gcc
and clang
gives similar error:
/tmp/x-54e820.o: In function `main':
x.cc:(.text+0xa): undefined reference to `K::a'
clang-3.7: error: linker command failed with exit code 1 (use -v to see invocation)
if I do following, it compiles without problem. Is this related to the way std::min
is written?
#include <iostream>
struct K{
const static int a = 5;
};
int main(){
K k;
std::cout << std::min((int) k.a, 7); // <= here is the change!!!
}
another way to avoid the error is if I do my own min()
:
template <class T>
T min(T const a, T const b){
return a < b ? a : b;
}
C-like preprocessor MIN
also works OK.
Upvotes: 9
Views: 235
Reputation: 354
Your have declared a static variable (a) in your struct, but you have not defined it.
struct K
{
const static int a; // declaration
};
const int K::a = 5; // definition
int main()
{
std::cout << std::min(K::a, 7);
}
You may find this link helpful.
I also agree with Richard Hodges answer.
Upvotes: -2
Reputation: 69892
This question is asked quite often. I believe it's a bug in clang. (see correction in comments)a
is being detected as a constant expression too early and the compiler is not generating a definition of it.
std::min
takes its arguments by const reference, so a definition must exist.
#include <iostream>
struct K{
const static int a = 5;
};
int main(){
K k;
std::cout << std::min(k.a, 7);
}
Here's a portable workaround alternative:
#include <iostream>
struct K{
constexpr static int a() { return 5; }
};
int main(){
K k;
std::cout << std::min(k.a(), 7);
}
Upvotes: 2
Reputation: 141618
std::min
accepts arguments by reference. Binding a reference to an object means that the object is odr-used (there is a code sample in [basic.def.odr]/2 pretty much the same as your sample).
However in the (int)k.a
case, k.a
is not odr-used; because it is performing lvalue-to-rvalue conversion which yields a constant expression. (There are a few other conditions here too but your code is OK).
If an object is odr-used then there must be exactly one definition of it; with no diagnostic required for violating this rule. So the first case may or may not be accepted; and the second case must be accepted.
In your own version of min
, it takes arguments by value, which is similar to the (int)k.a
case - the only action taken on k.a
there is rvalue conversion to initialize the parameter of your min
.
You can read the full set of rules about odr-use in section [basic.def.odr] of a C++ standard draft.
Upvotes: 8