16

I am baffled by the linker error when using the following code:

// static_const.cpp -- complete code
#include <vector>

struct Elem {
    static const int value = 0;
};

int main(int argc, char *argv[]) {
    std::vector<Elem> v(1);
    std::vector<Elem>::iterator it;

    it = v.begin();
    return it->value;
}

However, this fails when linking -- somehow it needs to have a symbol for the static const "value."

$ g++ static_const.cpp 
/tmp/ccZTyfe7.o: In function `main':
static_const.cpp:(.text+0x8e): undefined reference to `Elem::value'
collect2: ld returned 1 exit status

BTW, this compiles fine with -O1 or better; but it still fails for more complicated cases. I am using gcc version 4.4.4 20100726 (Red Hat 4.4.4-13).

Any ideas what might be wrong with my code?

hrr
  • 1,807
  • 2
  • 21
  • 35
  • 1
    possible duplicate of [Weird undefined symbols of static constants inside a struct/class](http://stackoverflow.com/questions/4891067/weird-undefined-symbols-of-static-constants-inside-a-struct-class) – karlphillip Apr 01 '11 at 01:22
  • Thanks for the useful link! It also shows an alternative solution, `struct Elem { enum { value = 0 }; }`, which seems pretty attractive. – hrr Apr 01 '11 at 01:45
  • possible duplicate of [C++ - defining static const integer members in class definition](http://stackoverflow.com/questions/3025997/c-defining-static-const-integer-members-in-class-definition) – ks1322 Mar 11 '14 at 08:52

6 Answers6

9

If you want to initialize it inside the struct, you can do it too:

struct Elem {
    static const int value = 0;
};

const int Elem::value;
karlphillip
  • 92,053
  • 36
  • 243
  • 426
  • This is cool! It works. It does not make sense to not initialize it in the structure (class) if you can (know the value at that point) because otherwise all the possible optimizations would be lost. (i.e. passing 0 directly instead of loading 0 from some memory address.) – Alexis Wilke Apr 29 '12 at 07:04
  • If this is really const, there is the alternative of the "enum hack", see: http://stackoverflow.com/questions/4891067/weird-undefined-symbols-of-static-constants-inside-a-struct-class The problems are also described there: before c++11 enums have no decent "type" like `int`, so std::min, std::make_pair etc. will not guess their template argument... – Tomasz Gandor Mar 04 '14 at 15:25
5

Try writing it as

struct Elem {
    static const int value;
};

const int Elem::value = 0;

etc

.

jonsca
  • 10,218
  • 26
  • 54
  • 62
  • 1
    This will unfortunately not work, when the value is a label in switch statement. – Petr Jun 08 '16 at 14:31
  • @Petr Sorry, not sure what to tell you there. I haven't used C++ in a few years, so I don't know if that's peculiar to the `static const` or what. – jonsca Jun 08 '16 at 23:14
  • 2
    I should have made myself more clear. I tried to point out to anyone reading you answer, that - although perfectly correct - there are cases when this will not work. For example, if you want to use the `value` as a label in switch, like `switch(x) { case value: break; }` – Petr Jun 09 '16 at 11:18
2

Also see this post: essentially, the problem is that somehow compiler ends up expanding your code into taking the address of Elem::value.

Community
  • 1
  • 1
Serge
  • 1,027
  • 14
  • 21
2

static class members are generally supposed to be defined outside the class (declared inside, defined outside) in one compilation unit.

I don't remember how that interacts with inline initialization of const static integral members.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • 1
    see http://stackoverflow.com/questions/1312241/using-a-static-const-int-in-a-struct-class/1312267#1312267 – J T Apr 01 '11 at 01:17
  • 1
    There's no special rule for the case where the declaration has an initializer---according to the standard, the declared object must be defined somewhere if it is potentially used. In practice, most compilers are defective in this regard, and will only generate an error for certain uses, not for others, if the definition is missing. (Which uses are never specified, and usually vary depending on the optimization level.) – James Kanze Apr 01 '11 at 10:25
1

Why not just do this?

return Elem::value;

But the answer is that you are assigning a value in the declaration. This is supposed to work for basic types such as int, and is only required for complex types (i.e. classes, such as if you had a string instead of int). What I have found in practice is that this is hit or miss depending on what version of what compiler you are using. And, as you found out, which optimization level.

  • That doesn't solve the initialization problem, though. Also, hit or miss often means undefined behavior. – jonsca Apr 01 '11 at 01:21
  • 4
    Thats just avoiding the question he's asking. – J T Apr 01 '11 at 01:22
  • Actually I did answer it. I said that what he is doing should work (i.e. there is nothing wrong), but in practice does not always work. He appears to have found one of those "does not work" cases. –  Apr 01 '11 at 01:24
  • 1
    No, the "does not work" is the general case. The `value` should really be defined somewhere, not only declared. If you just use the value as a compile time constant, it often "works anyway". – Bo Persson Apr 01 '11 at 07:32
1

In most of the compilers defining in-class static const <integral type> works fine. But some compilers like Android NDK, such in-class definitions results in linker errors. For such case, we may use the typed enums:

struct X
{
  enum : int64_t { VALUE = 100; }; // == static const int64_t VALUE = 100;
};
iammilind
  • 68,093
  • 33
  • 169
  • 336