5

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.

BCS
  • 75,627
  • 68
  • 187
  • 294
  • It's all in a header file that is included in several source files. – BCS Sep 26 '09 at 17:45
  • @Magnus Hoff: that has something to do with it. A test case in 2 files fails, but works if jammed into a single file. – BCS Sep 26 '09 at 18:03

2 Answers2

8

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.

mmmmmmmm
  • 15,269
  • 2
  • 30
  • 55
5

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
  • 257,169
  • 86
  • 333
  • 562
  • what does the inline do? (I thought that was part of an non-template corner of C++) – BCS Sep 26 '09 at 17:27
  • 2
    @BCS: `template` and `inline` are orthogonal. (However, while this might hide the error, it doesn't fix it at all.) – sbi Sep 26 '09 at 17:30
  • 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 Sep 26 '09 at 17:34
  • @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 Sep 26 '09 at 19:08
  • @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 Sep 27 '09 at 02:29
  • @sbi: It does fix the problem quite legally. BUT. I would use the solution proposed by rstevens. – Martin York Sep 27 '09 at 02:30