1
votes

Actual code is more complex but I was able to reduce it to this example.

Everything works fine until I try to take a pointer to MyPackets_t::types (uncomment call to foo() in main() )

At this point in order for the application to link, types requires a definition.

I'm struggling with correct syntax for the definition. Commented out template... should do the trick. However it generetes an error " template arguments to 'PacketCollection::types' do not match original template".

Trying something like this - template<> constexpr int PacketCollection::types[]; - causes another linker error as though variable is being used in that line rather than declared.

I've tried using MyPackets_t instead of PacketCollection - same results.

If I make packet collection non-templated then everything compiles and runs as expected.

I feel that I'm either missing something extremely basic here or there is a bug in the compiler. I get this behavior with gcc 4.8 and 4.9.

Clang 3.5 has a sligtly different take on the situation: declaration of constexpr static data member 'types' requires an initializer.

The only workaround that I've found so far was to use

static constexpr std::array<int, 2> types() { return {1,2}; }

instead, but I don't like this solution. If variable workes in non-templated version (using the initializer from header) it should also work for templated one.

#include <iostream>

using namespace std;

class Packet_A
{
public:
    static constexpr int TYPE = 1;
};

class Packet_B
{
public:
    static constexpr int TYPE = 2;
};

template <typename T> class PacketCollection
{
public:
    static constexpr size_t size = 2;
    static constexpr int types[size] { 1, 2 };
};

typedef PacketCollection<Packet_A> MyPackets_t;

//template<typename T> constexpr int PacketCollection<Packet_A>::types[PacketCollection<Packet_A>::size];

void foo(const int* bar, size_t size)
{
    if (size >= 2)
    {
        cout << bar[0] << bar[1];
    }
}

int main(int argc, char* argv[])
{
    cout << Packet_A::TYPE;
    cout << MyPackets_t::types[0];

    //foo(MyPackets_t::types, MyPackets_t::size);

    return 0;
}
1

1 Answers

2
votes

You should use T instead of Packet_A

template<typename T> constexpr int PacketCollection<T>::types[PacketCollection<T>::size];
                                                    ^                          ^

See live example.