Специализация шаблона по другому шаблону (того же класса) - PullRequest
6 голосов
/ 01 сентября 2011

Я пишу класс массива. Этот класс массива может снова содержать массивы в качестве членов. При реализации функции печати мне нужны специализации.

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
}

Последнее определение дает

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> >’

Версия g ++ - это g ++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3, если это имеет значение. Есть идеи, в чем проблема?

Заранее спасибо, Томас

Ответы [ 3 ]

4 голосов
/ 01 сентября 2011

В качестве альтернативы решению Дэвида вы можете безоговорочно переадресовать вызов на набор перегруженных функций:

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 */ }
3 голосов
/ 01 сентября 2011

Вы не можете частично специализировать функцию, вы можете только полностью специализировать ее, поэтому вы можете предоставить специализацию для double, но не для array<U>, где U является универсальным типа.

Вы можете обойти это ограничение, используя шаблон класса и частично специализируя его, но оно будет немного громоздким.

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>
      }
   }
}

А затем реализовать функцию-член в виде простой отправки с соответствующей перегрузкой:

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 );
   }
}

Конечно, в реальном коде все немного сложнее, и вам придется соответствующим образом упорядочить код и предоставить предварительные объявления шаблонов и все такое, но этого должно быть достаточно, чтобы начать работу.

1 голос
/ 01 сентября 2011

Ваша функция должна быть полностью специализированной. Например:

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

Однако ваш класс может быть частично специализированным. Например:

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;
};

- РЕДАКТИРОВАТЬ ---

Методы из обобщенного класса не будут автоматически «приняты» специализацией класса или наоборот. Однако вы можете использовать наследование, чтобы «автоматизировать» повторное использование методов из универсального класса. Например ...

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).

}

... что может или не может быть тем, что вам нужно (может потребоваться некоторое «ручное» повторение).

...