2017-02-17 13 views
2

У меня есть следующие классы:Как я специализируюсь/перегрузить функцию шаблона для шаблонного типа

template <class... T> 
class thing {}; 

template <class T> 
class container { 
    public: 
     container() { 
      std::cout << "normal constructor" << std::endl; 
     } 
}; 

Я могу написать полную специализацию для конструктора container<int> таким образом:

template <> 
container<int>::container() { 
    std::cout << "int constructor" << std::endl; 
} 

Мне хотелось бы иметь возможность определить аналогичный конструктор для container<thing<T>>. Я думаю, что я пытаюсь написать это частичная специализация функции шаблона Вот что я пытаюсь (что является незаконным.):

template <class T> 
container<thing<T>>::container() { 

} 

Это не компилируется.

Я не совсем уверен, как правильно решить эту проблему, и линии между тем, что перегрузка и специализация для функции класса шаблона становятся размытыми. Может ли это быть тривиально решено или потребуется ли type_traits (std::enable_if)? Как я могу это решить?

ответ

7

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

Может ли это быть тривиально решено или потребуется ли type_traits/enable_if? Как я могу это решить?

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

#include<iostream> 

template <class... T> 
class thing {}; 

template <class T> 
class container { 
    template<typename> 
    struct tag {}; 

    template<typename U> 
    container(int, tag<thing<U>>) { 
     std::cout << "thing<U>" << std::endl; 
    } 

    container(char, tag<T>) { 
     std::cout << "normal constructor" << std::endl; 
    } 

public: 
    container(): container(0, tag<T>{}) {} 
}; 

int main() { 
    container<int> c1; 
    container<thing<int>> c2{}; 
} 

видеть на wandbox.


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

#include<iostream> 

template <class... T> 
class thing {}; 

template<typename> struct tag {}; 
template<int N> struct prio: prio<N-1> {}; 
template<> struct prio<0> {}; 

template <class T> 
class container {  
    template<typename U> 
    container(prio<2>, tag<thing<U>>) { 
     std::cout << "thing<U>" << std::endl; 
    } 

    container(prio<1>, tag<double>) { 
     std::cout << "double" << std::endl; 
    } 

    container(prio<0>, tag<T>) { 
     std::cout << "normal constructor" << std::endl; 
    } 

public: 
    container(): container(prio<2>{}, tag<T>{}) {} 
}; 

int main() { 
    container<int> c1; 
    container<double> c2; 
    container<thing<int>> c3{}; 
} 

Посмотри на wandbox.

+1

очень элегантное решение (IMHO) – max66

+0

@ max66 Спасибо. Частичная специализация всего класса может быстро привести к дублированию кода, я обычно стараюсь избегать его, если это возможно. – skypjack

+0

@ max66 Добавлено больше деталей только в случае нескольких конструкторов _specialized_. Надеюсь, вам понравится. ;-) – skypjack

4

Вы не можете частичной специализации конструктор, но вы можете парциальное специализироваться полный класс

template <class T> 
class container<thing<T>> 
{ 
    public: 
     container() { } 
}; 
+0

таким образом мне придется полностью (повторно) определить класс, правильно? – user2079802

+0

@ пользователь2079802 - есть; но, если часть класса, которую вы должны (re) определить, мала, по сравнению с полным классом, вы можете создать небольшой базовый класс, содержащий только часть для определения/переопределения. – max66

+0

@ user2079802 - посмотрите также на решение skypjack: не производите частичную специализацию конструктора, но, объединяя конструкцию меток и делегирование, можете решить проблему без переопределения полного класса – max66