Почему шаблон функции не может быть частично специализированным?

Я знаю, что спецификация языка запрещает частичную специализацию шаблона функции.

Я хотел бы знать обоснование, почему оно запрещает это? Они не полезны?

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

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

Такой шаблон является жизнеспособной альтернативой отправке тегов.

Смотрите также

Статья Херба Саттера «Почему бы не специализироваться на шаблонах функций?»очень подробно объясняет вопросы, касающиеся явных специализаций. Это в равной степени относится и к гипотетической частичной специализации.