0

I inherit from a template with partial specialization, and I can't call the template ctor from the derived ctor.

When the partial specialization in the code below is commented out, it compiles without any errors or warnings.

#include <iostream>
typedef enum {supertype, subtype} selector;

template< typename T, selector s>
class Tmpl {
protected:
    T* root;
public:
    Tmpl( T* t = 0 ) {
        root = t;
    }
    T listHead( ) {
        std::cout << "template listHead() called" << std::endl;
    }
};

class Descriptor {
public:
    Descriptor( const char * s ) {
        std::cout << "Descriptor " << s << std::endl;
    }
};

// partial specialization - if uncommented, errors 
// are reported at the supertypesIterator ctor below.
/*
template<selector s>
class Tmpl<Descriptor, s> {
public:
    Descriptor listHead( ) {
        switch( s ){
            case supertype:
                return Descriptor("Supertypes");
            case subtype:
                return Descriptor("Subtypes");
        }
    }
};
*/

class supertypesIterator : public Tmpl<Descriptor, supertype> {
public:
    supertypesIterator( Descriptor* t = 0 ):Tmpl<Descriptor, supertype>(t) {}
};


main() {
    supertypesIterator s;
    s.listHead();
}

If I uncomment the specialization, I get the following errors:

$ g++ trouble.cc

trouble.cc: In constructor ‘supertypesIterator::supertypesIterator(Descriptor*)’:
trouble.cc:43:74: error: no matching function for call to ‘Tmpl<Descriptor, (selector)0u>::Tmpl(Descriptor*&)’
trouble.cc:43:74: note: candidates are:
trouble.cc:27:7: note: Tmpl<Descriptor, (selector)0u>::Tmpl()
trouble.cc:27:7: note:   candidate expects 0 arguments, 1 provided
trouble.cc:27:7: note: Tmpl<Descriptor, (selector)0u>::Tmpl(const Tmpl<Descriptor, (selector)0u>&)
trouble.cc:27:7: note:   no known conversion for argument 1 from ‘Descriptor*’ to ‘const Tmpl<Descriptor, (selector)0u>&’

What do I need to do to be able to initialize the base class from within the supertypesIterator ctor?

I'm using g++ version 4.7.1, though I'll also need this to work cross-platform.

Mark
  • 1,035
  • 2
  • 11
  • 24

1 Answers1

2

You have to implement the missing constructor in your specialization. Otherwise, the constructor for supertypesIterator is trying to call a constructor for Tmpl that doesn't exist.

template<selector s>
class Tmpl<Descriptor, s> {
    Descriptor* root;
public:
    Tmpl( Descriptor* t = 0 ) {
        root = t;
    }
    Descriptor listHead( ) {
        switch( s ){
            case supertype:
                return Descriptor("Supertypes");
            case subtype:
                return Descriptor("Subtypes");
        }
    }
};
jxh
  • 69,070
  • 8
  • 110
  • 193
  • Aha! Moments ago, I tried adding the ctor `Tmpl( Descriptor* t = 0 ): Tmpl(t) {}` to the specialization, but that didn't work ("warning: delegating constructors only available with..."). Your version does work. So template specializations _are_ treated similarly to derived classes... I thought they weren't. – Mark Jul 30 '12 at 23:02
  • @Mark: A template specialization is a re-implementation of the template for the set of parameters you provide. So, it is kind of a bummer if there is a lot of overlap, but it does provide full flexibility. – jxh Jul 30 '12 at 23:09
  • It seems there isn't any way to call the unspecialized constructor from the constructor within the specialization? – Mark Jul 30 '12 at 23:13
  • Never mind my last question, I think I found the answer [here](http://stackoverflow.com/a/605530/382458) – Mark Jul 30 '12 at 23:25
  • @Mark: You would have to create an abstraction if you want code reuse. For instance, one could create a template specialization for a container so that a pointer type would reuse code from the regular template that was instantiated from a `void *`. The formatting is ugly, but the general idea is described [here](http://www.linuxtopia.org/online_books/programming_books/c++_practical_programming/c++_practical_programming_122.html). – jxh Jul 30 '12 at 23:27
  • @Mark: Two specializations of a template are two completely unrelated types. In your case, the specialization for `Descriptor` consists of only a member function. You cannot delegate to the constructor of the generic template in the same way that the constructor of `std::string` cannot delegate to the constructor of `std::vector`: they share nothing (in the case of the template there is very little sharing: the same base template (don't confuse with base class), and the same name). If you want to change only a small subset to the functionality of a template, consider (ab)using inheritance – David Rodríguez - dribeas Jul 31 '12 at 00:33