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

87

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

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

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!
Наваз
источник
Ибо template<typename T, typename U> void f(T t, U u) {}также template<> void f(int t, char u) {}разрешено.
dashesy
10
Мне интересно, что люди продолжают предлагать обходные пути, когда вопрос не в том, «как я могу достичь подобной цели», а в том, «каково обоснование такого поведения» ... Я сам не знаю причину этого выбора, но предполагаю, что у комитета должна была быть причина запретить частичную специализацию шаблона функции. Пока что «ближайшим» объяснением является ссылка, размещенная Георгием, которая только указывает на потенциальные «риски» частичной специализации шаблона функции при наличии перегрузок. Однако я не думаю, что это причина для запрета этой функции, поэтому я предполагаю, что это еще не все ..
bartgol

Ответы:

59

AFAIK, который изменился в С ++ 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
}
Приветствия и hth. - Альф
источник
у вас в n3225 стандарт стоит? Я быстро поискал, но не нашел: /
Matthieu M.
1
ах извините ... не хватает слова. У меня есть документ, но я не могу найти конкретный абзац . Хотя с учетом твоего редактирования, я думаю, это просто потому, что его там нет :)
Матье М.
3
Это не изменилось в C ++ 0x. Я тоже сомневаюсь в его полезности. Вы всегда можете перегрузить шаблон и использовать частичное упорядочение .
Йоханнес Шауб - лит
1
Позднее обновление: не изменилось даже в C ++ 17 восемь лет спустя и, похоже, не входит в C ++ 20. Хотя не вижу причин для ...
Аконкагуа
Я считаю, что это наиболее полная реализация концепции
Виктор
18

Что ж, вы действительно не можете выполнить частичную специализацию функции / метода, но вы можете выполнить перегрузку.

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

Это способ, но я не знаю, удовлетворяет ли он вас.

Михал В
источник
1
Вау, мой разум был заполнен шаблонами, и я действительно забыл, насколько простыми могут быть вещи :)
Йоханнес,
1
К сожалению, это не тот случай, когда вы хотите передать вариативные аргументы после частичной специализации функции .. :(
Гвангму Ли
Я не уверен, что имелось в виду, передавая вариативные шаблоны, поэтому я хотел бы знать, чем это отличается от частичной специализации. Не могли бы вы предоставить более подробную информацию?
beginpluses
Что, если вам нужно всего две функции для всех целочисленных и плавающих типов?
Дмитрий Докшин,
14

В общем, специализировать шаблоны функций не рекомендуется из-за проблем с перегрузкой. Вот хорошая статья из журнала пользователей C / C ++: http://www.gotw.ca/publications/mill17.htm

И в нем честный ответ на ваш вопрос:

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

Георгий Пашков
источник
3
В статье не говорится о частичной специализации, кроме того, что она упоминается один раз.
Эури Пинхоллоу
11

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

#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 ;
}
Кей Ф. Янке
источник
1
Затем вы можете использовать один шаблон функции для выполнения вызовов, избавившись от уродливого ()()синтаксиса.
tmr232 04