views:

206

answers:

2

I have a template class that looks something like this:

template<class T> class C
{
    void A();
    void B();

    // Other stuff
};

template<class T> void C<T>::A() { /* something */ }
template<class T> void C<T>::B() { /* something */ }

What I want is to provide an explicit specialization for only A while retaining the default for B and the "other stuff".

What I have tried so far is

class D { };
template<> void C<D>::A() { /*...*/ } // Gives a link error: multiple definition

Every other variant I've attempted fails with parse errors.


What I did:

The original problem was that the explicit specialization was in a header file so it was getting dumped into several object files and messing up the link (Why doesn't the linker notice all the instances of the symbol are the same a just shut up?)

The solution ends up being to move the explicit specialization from the header file to a code file. However to make the other users of the header file not instance the default version, I needed to place a prototype back in the header. Then to get GCC to actually generate the explicit specialization, I needed to place a dummy variable of the correct type in the code file.

+3  A: 

Try

template<> inline void c<int>::A() { ... }
//         ^^^^^^

As you have defined it in a header file. Each source file that sees it will build an explicit version of it. This is resulting in your linking errors. So jsut declare it as inline.

Martin York
what does the inline do? (I thought that was part of an non-template corner of C++)
BCS
@BCS: `template` and `inline` are orthogonal. (However, while this might hide the error, it doesn't fix it at all.)
sbi
It does make things compile (and seem to run correctly). OTOH I forgot to point out that the function in question is virtual (and from a base class) so I'm not sure it can be inlined. So who knows what is going on.
BCS
@BCS what goes on is exactly what Martin described, of course. The function is inline, so it's similar to the non-template case of "struct F { void g() { } };" which will not throw an error (g is inline) while "struct F { void g(); }; void F::g() { }" will throw a "multiple definition" error (g is not inline). Inline functions can be defined multiple times.
Johannes Schaub - litb
@BCS: don't take the inline keyword literally. The 'act' of inlining is up-to the compiler. But a function marked inline may have multiple physical implementations across compilation units without causing the linker to complain.
Martin York
@sbi: It does fix the problem quite legally. BUT. I would use the solution proposed by rstevens.
Martin York
+4  A: 

Alternatively to Martin York's inline solution you could also do in your header file:

class D { };
template<> void C<D>::A(); // Don't implement here!

And supply a .cpp file with the implementation:

template<> void C<D>::A() { /* do code here */ }

So you avoid the multiple definitions by supplying a single one. This is also good to hide implementations for specific Types away from the template header file when publishing the library.

rstevens
That's exactly what I ended up doing so you get the check mark.
BCS