Thomas Handorf
Thomas Handorf

Reputation: 361

Template specialization by another template (of same class)

I'm writing an array class. This array class can contain again arrays as members. When implementing a printing function, I need specializations.

26:template <class T> class array : public vector<T>{
public:
    ...
       string* printToString();
    ...
};
...           
template <class T> string* array<T>::printToString(){
   ...  // generic function
}
template <> inline string* array<double>::printToString(){
   ...  // spezialization for double, works
}
561:template <class U> string* array<array<U>*>::printToString(){
   ...  // does not work
}

The last definition produces

src/core/array.h:561: error: invalid use of incomplete type ‘class array<array<T> >’
src/core/array.h:26: error: declaration of ‘class array<array<T> >’

The g++ version is g++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3 if that matters. Any ideas what's the problem?

Thanks in advance, Thomas

Upvotes: 6

Views: 3413

Answers (3)

MSalters
MSalters

Reputation: 180245

As an alternative to David's solution, you can unconditionally forward the call to a set of overloaded functions:

template <class T> class array;
namespace details {
  template <class T> std::string array_print(array<T> const&);
  std::string array_print(array<double> const&); // Regular function 
  template <class T> std::string array_print(array<array<T> > const&);
}

template <class T> class array : private vector<T> {
public:
    ...
       std::string printToString() { return details::array_print(*this); }
    ...
};

namespace details { /* implementions after class is defined */ }

Upvotes: 4

Branko Dimitrijevic
Branko Dimitrijevic

Reputation: 52157

Your function must be fully specialized. For example:

// Fully specialized. You cannot replace `double` with generic parameter.
template <>
string* array<array<double>*>::printToString(){
    return nullptr;
}

However, your class can be partially specialized. For example:

template <class T> class array : public vector<T>{
public:
    string* printToString();
};

template <class T> string* array<T>::printToString(){
    return nullptr;
};

// Partial specialization.
template <class T> class array<array<T>*> : public vector<T>{
public:
    string* printToString();
};

template <class T> string* array<array<T>*>::printToString(){
    return nullptr;
};

-- EDIT ---

The methods from generic class will not be automatically "taken" by the class specialization, or vice-versa. You can, however use inheritance to "automate" the reuse of methods from generic class. For example...

template <class T> class array : public vector<T>{
public:
    string* printToString();
    void f();
};

// (1a)
template <class T> string* array<T>::printToString(){
    return nullptr;
};

// (2)
template <class T> void array<T>::f(){
};

template <class T> class array<array<T>*> : public array<T> {
public:
    string* printToString();
};

// (1b)
template <class T> string* array<array<T>*>::printToString(){
    return nullptr;
};

void Test() {

    array<double> a1;
    a1.printToString(); // Calls (1a).
    a1.f(); // Calls (2).

    array<array<char>*> a2;
    a2.printToString(); // Calls (1b).
    a2.f(); // Calls (2).

}

...which may or may not be what you need (some "manual" repetition might be necessary).

Upvotes: 1

You cannot partially specialize a function, you can only fully specialize it, which is the reason why you can provide an specialization for double but not for array<U> where U is a generic type.

You can get around this limitation by using a class template, and partially specializing that, but it will be a bit cumbersome.

namespace detail {
   template <typename T>
   struct array_printer {
      static std::string print( array<T> const & array ) {
         // basic implementation
      }
   };
   template <typename T>
   struct array_printer< array<T> > {
      static std::string print( array< array<T> > const & array ) {
         // specialization for array<T>
      }
   }
}

And then implement the member function as a simple dispatch to the appropriate overload:

template <typename T> 
class array : std::vector<T> {  // do not publicly derive from STL containers
public:
   std::string printToString() const {
      return detail::array_printer<T>::print( *this );
   }
}

Of course, things are a little more complex in real code, and you will have to order the code appropriatedly, and provide forward declarations of the templates and all that, but this should be enough to get you started.

Upvotes: 3

Related Questions