3

Trying to initialize a static map. From other questions I've gathered that it must be done outside of the header file, and in c++11 can be done with uniform initialization. But when I try to use another static member, I get problems:

foo.h:

#include <map>

class TestSuite {
    static constexpr int x = 3;
    static std::map<int, int> v; 
};

foo.cpp:

#include "foo.h"

std::map<int, int> TestSuite::v = {{x, 5}};

int main() {
    TestSuite t;
}

Then I get the error

In function `__static_initialization_and_destruction_0(int, int)':
foo.cpp:(.text+0x4b): undefined reference to `TestSuite::x'
collect2: error: ld returned 1 exit status

3 Answers3

4

I cannot reproduce this problem with GCC 6.1.0. However, it can be reproduced any time you try to bind a reference to a constexpr variable you haven't defined, which is probably what your std::map constructor does :

struct Foo {
    static constexpr int i = 42;
};

int main() {
    auto const &p = Foo::i; // undefined reference to `Foo::i'
}

This is because binding the reference is an ODR-use of i, which requires a unique definition to exist at link-time.

There is a simple workaround that works in most such cases, and that is to apply a unary +:

struct Foo {
    static constexpr int i = 42;
};

int main() {
    auto const &p = +Foo::i; // OK!
}

Applying + does not ODR-use i, since only its value is needed, not its identity. Then the reference binds to the temporary returned by +, instead of i.

Quentin
  • 62,093
  • 7
  • 131
  • 191
3

You also need to define TestSuite::x. Put this in your cpp file:

const int TestSuite::x;
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
1

Another solution.

Change the static member variable to a static member function.

class TestSuite {
    static constexpr int getX() { return 3;}
    static std::map<int, int> v; 
};

and

std::map<int, int> TestSuite::v = {{getX(), 5}};
R Sahu
  • 204,454
  • 14
  • 159
  • 270