1

I use gcc 4.7.3 for ARM platform to compile my code. I have several classes like this:

// types.h
enum Types
{
    kType1,
    kType2
    // ...
};

// d1.h
class D1 : public Base
{
public:
    static const int type = kType1;
    // ...
};

// d2.h
class D2 : public Base
{
public:
    static const int type = kType2;
    // ...
};

Somewhere in the sources I use those classes:

MyObject obj;
doSomething<D1>(obj);
doSomething<D2>(obj);

// other.cpp
class Foo
{
    template<typename T>
    void doSomething(MyObject obj)
    {
        mm_.insert(std::multimap<int, MyObject>::value_type(T::type, obj));
    }
};

And get the next messages (during linking):

undefined reference to `D1::kType`
undefined reference to `D2::kType`
// more messages of this type

OK. If I change do_something function like this:

template<typename T>
void doSomething(MyObject obj)
{
    mm_.insert(std::multimap<int, MyObject>::value_type( (int) T::type, obj));
}

it compiles OK. But why? Can't find anything in the standard about it. Does anybody have ideas about what's going on?

Thanks.

P.S.

This fix

 // d1.cpp
 const int D1::kType;

also works, but this is expected.

P.P.S. the answer would be quite obvious in case of using reference or pointer to T::type, but I don't see anything that requires a ref or ptr. AFAIK std::multimap::value_type take the arguments by value (not ref, nor ptr).

11
  • 2
    Shouldn't you be referencing T::type and not T::kType? Commented Jul 3, 2013 at 14:33
  • Are you sure what Timo pointed out doesn't actually fix the problem? Since it seems like the linker is looking for D1::kType, which is exactly what your template was asking for. Commented Jul 3, 2013 at 14:36
  • @MatsPetersson, It was just a typo in the question. The real code uses right member.
    – maverik
    Commented Jul 3, 2013 at 14:38
  • You might have to post the real code then as the code above looks perfectly harmless and OK, at least to me. Commented Jul 3, 2013 at 14:39
  • 1
    std::multimap<T,U>::value_type is std::pair<const T,U> whose constructor takes const& T, const& U. FWIW.
    – rici
    Commented Jul 3, 2013 at 14:49

1 Answer 1

1
// d1.cpp
const int D1::kType;

// d2.cpp
const int D2::kType;

is the answer,

//d1.h
public:
    static const int type = kType1;

Is like a function prototype, it is compiled into every complication object (cpp file) that includes the header, and so because of the ODR rule it doesn't actually reserve the memory space, or else the linker would find lots of instances of the variable in every compilation unit which included the header.

You therefore need one compilation unit (a cpp file) which defines the actual memory space that will be used for the class constant found in the header that is used multiple times.

Not the answer you're looking for? Browse other questions tagged or ask your own question.