Nick
Nick

Reputation: 10539

Strange C++ link error

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

Answers (3)

rboc
rboc

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

Richard Hodges
Richard Hodges

Reputation: 69892

This question is asked quite often. I believe it's a bug in clang. a is being detected as a constant expression too early and the compiler is not generating a definition of it. (see correction in comments)

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

M.M
M.M

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

Related Questions