Почему шаблон функции не может быть частично специализированным?
Я знаю, что спецификация языка запрещает частичную специализацию шаблона функции.
Я хотел бы знать обоснование, почему оно запрещает это? Они не полезны?
template<typename T, typename U> void f() {} //allowed!
template<> void f<int, char>() {} //allowed!
template<typename T> void f<char, T>() {} //not allowed!
template<typename T> void f<T, int>() {} //not allowed!
5 ответов
AFAIK, который изменился в C++0x.
Я предполагаю, что это был просто недосмотр (учитывая, что вы всегда можете получить эффект частичной специализации с большим количеством подробного кода, поместив функцию как static
член класса).
Вы можете посмотреть соответствующий DR (Отчет о дефектах), если таковой имеется.
РЕДАКТИРОВАТЬ: проверяя это, я нахожу, что другие тоже так считают, но никто не может найти такую поддержку в проекте стандарта. Этот поток SO, кажется, указывает на то, что частичная специализация шаблонов функций не поддерживается в C++ 0x.
РЕДАКТИРОВАТЬ 2: просто пример того, что я имел в виду, "поместив функцию как static
член класса ":
#include <iostream>
using namespace std;
// template<typename T, typename U> void f() {} //allowed!
// template<> void f<int, char>() {} //allowed!
// template<typename T> void f<char, T>() {} //not allowed!
// template<typename T> void f<T, int>() {} //not allowed!
void say( char const s[] ) { std::cout << s << std::endl; }
namespace detail {
template< class T, class U >
struct F {
static void impl() { say( "1. primary template" ); }
};
template<>
struct F<int, char> {
static void impl() { say( "2. <int, char> explicit specialization" ); }
};
template< class T >
struct F< char, T > {
static void impl() { say( "3. <char, T> partial specialization" ); }
};
template< class T >
struct F< T, int > {
static void impl() { say( "4. <T, int> partial specialization" ); }
};
} // namespace detail
template< class T, class U >
void f() { detail::F<T, U>::impl(); }
int main() {
f<char const*, double>(); // 1
f<int, char>(); // 2
f<char, double>(); // 3
f<double, int>(); // 4
}
Ну, вы действительно не можете выполнять частичную специализацию функций / методов, но можете перегружаться.
template <typename T, typename U>
T fun(U pObj){...}
// acts like partial specialization <T, int> AFAIK
// (based on Modern C++ Design by Alexandrescu)
template <typename T>
T fun(int pObj){...}
Это путь, но я не знаю, удовлетворит ли он вас.
В общем случае не рекомендуется специализировать шаблоны функций из-за проблем с перегрузкой. Вот хорошая статья из C / C++ Users Journal: http://www.gotw.ca/publications/mill17.htm
И в нем содержится честный ответ на ваш вопрос:
Во-первых, вы не можете частично специализировать их - в значительной степени только потому, что язык говорит, что вы не можете.
Поскольку вы можете частично специализировать классы, вы можете использовать функтор:
#include <iostream>
template < typename dtype , int k > struct fun
{
int operator()()
{
return k ;
}
} ;
template < typename dtype > struct fun < dtype , 0 >
{
int operator()()
{
return 42 ;
}
} ;
int main ( int argc , char * argv[] )
{
std::cout << fun<float,5>()() << std::endl ;
std::cout << fun<float,0>()() << std::endl ;
}
Частичная специализация шаблонов функций была бы чрезвычайно проблематичной по двум причинам.
Синтаксическая неоднозначность с перегрузкой шаблона функции
Рассмотрим синтаксис полной специализации:
template <typename T>
void foo(T);
template <>
void foo(int); // full specialization; we could optionally write foo<int>(int)
Из
template <typename T>
void foo(std::complex<T>);
Это можно интерпретировать либо как вторую перегрузку, либо как частичную специализацию.
Даже полная специализация может сбить с толку, если имеется несколько перегрузок; см. Какая перегрузка выбирается при определении явной специализации шаблона функции?
Специализации не участвуют в разрешении перегрузки
template <typename T>
void foo(); // (0) primary template
template <>
void foo(std::complex<int>); // (1) full specialization
template <typename T>
void foo(std::complex<T>); // (2) overload
Если мы вызываем это с аргументом типа , то вызывается (2), а не (1), поскольку специализации не участвуют в разрешении перегрузки. Это противоречит здравому смыслу, потому что
Полная специализация и гипотетическая частичная специализация шаблонов функций ведут себя вопреки интуиции. Перегрузка шаблонов функций — гораздо более полезная функция, и в большинстве случаев ей следует отдавать предпочтение.
Обходной путь: перегрузка или частичная специализация шаблонов классов.
В большинстве случаев вместо этого вы можете просто использовать перегрузку шаблона функции. Если вам нужно точно имитировать частичную специализацию, вместо этого вы можете написать функциональные объекты:
// primary template
template <typename T>
struct foo_object {
void operator()(T) const { /* ... */ }
};
// partial specialization
template <typename T>
struct foo_object<std::complex<T>> {
void operator()(std::complex<T>) const { /* ... */ }
};
// convenience function
template <typename T>
void foo(T t) {
return foo_object<T>{}(t);
}
На первый взгляд это может показаться бессмысленным по сравнению с перегрузкой шаблона функции, но частичные специализации могут делегировать друг другу посредством наследования, что может быть полезно.
Такой шаблон является жизнеспособной альтернативой отправке тегов.
Смотрите также
Статья Херба Саттера «Почему бы не специализироваться на шаблонах функций?»очень подробно объясняет вопросы, касающиеся явных специализаций. Это в равной степени относится и к гипотетической частичной специализации.