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).

David G
  • 94,763
  • 41
  • 167
  • 253
maverik
  • 5,508
  • 3
  • 35
  • 55
  • 2
    Shouldn't you be referencing T::type and not T::kType? – Timo Geusch Jul 03 '13 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. – Mats Petersson Jul 03 '13 at 14:36
  • @MatsPetersson, It was just a typo in the question. The real code uses right member. – maverik Jul 03 '13 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. – Timo Geusch Jul 03 '13 at 14:39
  • Related [question](http://stackoverflow.com/questions/5391973/undefined-reference-to-static-const-int?rq=1). – maverik Jul 03 '13 at 14:39
  • @maverik please provide an [SSCCE](http://sscce.org). I tried to extend the bits and pieces you left us, but gcc compiled it quite fine: http://ideone.com/CTu1UD - so gcc-4.7.2 could not spot an error, neither can we, at least not by looking at the code and guessing what's missing. – Arne Mertz Jul 03 '13 at 14:40
  • @TimoGeusch, unfortunately it's big enough and it's hard to cut it to minimal compilable example. But I'll try – maverik Jul 03 '13 at 14:40
  • In cutting it down, chances are pretty good that you will find the actual problem - it's quite likely a typo similar to the one you had just now. – Mats Petersson Jul 03 '13 at 14:42
  • 1
    `std::multimap::value_type` is `std::pair` whose constructor takes `const& T, const& U`. FWIW. – rici Jul 03 '13 at 14:49
  • The actual problem is that many pieces of this code is in the header files. So it is hard enough to make a simple example to show the case. And if I put those stuff in cpp then this wouldn't be relevant. – maverik Jul 03 '13 at 14:50
  • @rici: now it makes sense (and I can understand why cast solves the problem). – maverik Jul 03 '13 at 14:51

1 Answers1

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.

Strings
  • 1,674
  • 10
  • 16