Частичная специализация шаблона функции C++?
Я знаю, что приведенный ниже код является частичная специализация класса:
template <typename T1, typename T2>
class MyClass {
…
};
// partial specialization: both template parameters have same type
template <typename T>
class MyClass<T,T> {
…
};
также я знаю, что C++ не разрешает частичную специализацию шаблона функции (допускается только полная). Но означает ли мой код, что я частично специализировал свой шаблон функции для Аргументов одного/того же типа? Потому что он работает для Microsoft Visual Studio 2010 Express! Если нет, то не могли бы вы объяснить концепцию частичной специализации?
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
template <typename T1, typename T2>
inline T1 max (T1 const& a, T2 const& b)
{
return a < b ? b : a;
}
template <typename T>
inline T const& max (T const& a, T const& b)
{
return 10;
}
int main ()
{
cout << max(4,4.2) << endl;;
cout << max(5,5) << endl;
int z;
cin>>z;
}
6 ответов
поскольку частичная специализация не допускается , как указывали другие ответы, вы можете обойти ее, используя std::is_same
и std::enable_if
, как показано ниже:
template <typename T, class F>
inline typename std::enable_if<std::is_same<T, int>::value, void>::type
typed_foo(const F& f) {
std::cout << ">>> messing with ints! " << f << std::endl;
}
template <typename T, class F>
inline typename std::enable_if<std::is_same<T, float>::value, void>::type
typed_foo(const F& f) {
std::cout << ">>> messing with floats! " << f << std::endl;
}
int main(int argc, char *argv[]) {
typed_foo<int>("works");
typed_foo<float>(2);
}
выход:
$ ./a.out
>>> messing with ints! works
>>> messing with floats! 2
редактировать: в случае, если вам нужно иметь возможность обрабатывать все оставшиеся случаи, вы можете добавить определение, в котором говорится, что уже обработанные случаи не должны матч -- в противном случае вы попадете в неоднозначные определения. Определение может быть:
template <typename T, class F>
inline typename std::enable_if<(not std::is_same<T, int>::value)
and (not std::is_same<T, float>::value), void>::type
typed_foo(const F& f) {
std::cout << ">>> messing with unknown stuff! " << f << std::endl;
}
int main(int argc, char *argv[]) {
typed_foo<int>("works");
typed_foo<float>(2);
typed_foo<std::string>("either");
}
что производит:
$ ./a.out
>>> messing with ints! works
>>> messing with floats! 2
>>> messing with unknown stuff! either
несмотря на это все-делам вещь выглядит немного скучной, так как вы должны рассказать компилятору все, что вы уже сделали, вполне выполнимо обработать до 5 или еще несколько специализаций.
что такое специализация ?
Если вы действительно хотите понять, шаблоны, вы должны взглянуть на функциональных языках. Мир шаблонов в C++ - это чисто функциональный собственный язык.
в функциональных языках, выбор делается с помощью Шаблоны:
-- An instance of Maybe is either nothing (None) or something (Just a)
-- where a is any type
data Maybe a = None | Just a
-- declare function isJust, which takes a Maybe
-- and checks whether it's None or Just
isJust :: Maybe a -> Bool
-- definition: two cases (_ is a wildcard)
isJust None = False
isJust Just _ = True
Как видите, мы перегрузка определение isJust
.
Ну, шаблоны классов C++ работают точно так же. Вы предоставляете main объявление, в котором указано количество и характер параметров. Это может быть просто объявление или также действует как определение (ваш выбор), а затем вы можете (если хотите) предоставить специализации шаблона и связать с ними другую (в противном случае это было бы глупо) версию класса.
для функций шаблона специализация несколько более неудобна: она несколько конфликтует с разрешением перегрузки. Как таковой, оно было решено, что специализация будет относиться к неспециализированному варианту, и специализации не будут рассматриваться в ходе разрешения перегрузки. Поэтому алгоритмом выбора правильной функции становится:
- выполните разрешение перегрузки, среди регулярных функций и неспециализированных шаблонов
- если выбран неспециализированный шаблон, проверьте, существует ли для него специализация, которая будет лучше соответствовать
(для on углубленное лечение, см. GotW #49)
таким образом, шаблонная специализация функций является гражданином второй зоны (буквально). Что касается меня, нам было бы лучше без них: мне еще предстоит столкнуться с тем, что использование специализации шаблона не может быть решена с перегрузкой.
это специализация шаблона ?
нет, это просто перегрузка, и это нормально. На самом деле, перегрузки обычно работают как мы и ожидаем, в то время как специализации могут быть удивительными (Помните статью GotW, которую я связал).
нет. Например, вы можете легально специализироваться std::swap
, но вы не можете юридически определить свою собственную перегрузку. Это означает, что вы не можете сделать std::swap
работа для собственного шаблона пользовательского класса.
перегрузка и частичная специализация может иметь тот же эффект в некоторых случаях, но далеко не все.
неклассовая, непеременная частичная специализация не допускается, но, как сказано:
все проблемы в компьютере наука может быть решена путем еще один уровень косвенности. -- Дэвид Уилер!--2-->
добавление класса для переадресации вызова функции может решить эту проблему, вот пример:
template <class Tag, class R, class... Ts>
struct enable_fun_partial_spec;
struct fun_tag {};
template <class R, class... Ts>
constexpr R fun(Ts&&... ts) {
return enable_fun_partial_spec<fun_tag, R, Ts...>::call(
std::forward<Ts>(ts)...);
}
template <class R, class... Ts>
struct enable_fun_partial_spec<fun_tag, R, Ts...> {
constexpr static R call(Ts&&... ts) { return {0}; }
};
template <class R, class T>
struct enable_fun_partial_spec<fun_tag, R, T, T> {
constexpr static R call(T, T) { return {1}; }
};
template <class R>
struct enable_fun_partial_spec<fun_tag, R, int, int> {
constexpr static R call(int, int) { return {2}; }
};
template <class R>
struct enable_fun_partial_spec<fun_tag, R, int, char> {
constexpr static R call(int, char) { return {3}; }
};
template <class R, class T2>
struct enable_fun_partial_spec<fun_tag, R, char, T2> {
constexpr static R call(char, T2) { return {4}; }
};
static_assert(std::is_same_v<decltype(fun<int>(1, 1)), int>, "");
static_assert(fun<int>(1, 1) == 2, "");
static_assert(std::is_same_v<decltype(fun<char>(1, 1)), char>, "");
static_assert(fun<char>(1, 1) == 2, "");
static_assert(std::is_same_v<decltype(fun<long>(1L, 1L)), long>, "");
static_assert(fun<long>(1L, 1L) == 1, "");
static_assert(std::is_same_v<decltype(fun<double>(1L, 1L)), double>, "");
static_assert(fun<double>(1L, 1L) == 1, "");
static_assert(std::is_same_v<decltype(fun<int>(1u, 1)), int>, "");
static_assert(fun<int>(1u, 1) == 0, "");
static_assert(std::is_same_v<decltype(fun<char>(1, 'c')), char>, "");
static_assert(fun<char>(1, 'c') == 3, "");
static_assert(std::is_same_v<decltype(fun<unsigned>('c', 1)), unsigned>, "");
static_assert(fun<unsigned>('c', 1) == 4, "");
static_assert(std::is_same_v<decltype(fun<unsigned>(10.0, 1)), unsigned>, "");
static_assert(fun<unsigned>(10.0, 1) == 0, "");
static_assert(
std::is_same_v<decltype(fun<double>(1, 2, 3, 'a', "bbb")), double>, "");
static_assert(fun<double>(1, 2, 3, 'a', "bbb") == 0, "");
static_assert(std::is_same_v<decltype(fun<unsigned>()), unsigned>, "");
static_assert(fun<unsigned>() == 0, "");
поздний ответ, но некоторые поздние читатели могут найти его полезным: иногда вспомогательная функция, разработанная таким образом, что она может быть специализированной, также может решить проблему.
Так давайте представим, это то, что мы пробовал решение:
template <typename R, typename X, typename Y>
void function(X x, Y y)
{
R* r = new R(x);
f(r, y); // another template function?
}
// for some reason, we NEED the specialization:
template <typename R, typename Y>
void function<R, int, Y>(int x, Y y)
{
// unfortunately, Wrapper has no constructor accepting int:
Wrapper* w = new Wrapper();
w->setValue(x);
f(w, y);
}
OK, частичная специализация функции шаблона, мы не можем этого сделать... Поэтому давайте "экспортировать" часть, необходимую для специализации, в вспомогательную функцию, специализировать ее и использовать:
template <typename R, typename T>
R* create(T t)
{
return new R(t);
}
template <>
Wrapper* create<Wrapper, int>(int n) // fully specialized now -> legal...
{
Wrapper* w = new Wrapper();
w->setValue(n);
return w;
}
template <typename R, typename X, typename Y>
void function(X x, Y y)
{
R* r = create<R>(x);
f(r, y); // another template function?
}
этот can быть интересным, особенно если альтернативы (обычные перегрузки вместо специализации,решение предложил Рубенса ... - не то чтобы они плохие или мои лучше, просто другое one) будет иметь довольно много общего кода.