Dlaczego szablon funkcji nie może być częściowo wyspecjalizowany?

87

Wiem, że specyfikacja języka zabrania częściowej specjalizacji szablonu funkcji.

Chciałbym poznać uzasadnienie, dlaczego tego zabrania? Czy nie są przydatne?

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!
Nawaz
źródło
Bo template<typename T, typename U> void f(T t, U u) {}też template<> void f(int t, char u) {}jest dozwolone.
dashesy
10
Uważam za interesujące, że ludzie wciąż zapewniają obejścia, gdy pytanie nie brzmi „jak mogę osiągnąć podobny cel”, ale „jakie jest uzasadnienie tego zachowania” ... Sam nie znam powodu tego wyboru, ale zakładam, że komitet musiał mieć powód, by zabronić częściowej specjalizacji szablonu funkcji. Jak dotąd „najbliższym” wyjaśnieniem jest link opublikowany przez Georgy'ego, który wskazuje tylko potencjalne „ryzyko” częściowej specjalizacji szablonów funkcji, gdy występują przeciążenia. Jednak nie sądzę, żeby to był powód do zakazania tej funkcji, więc zakładam, że jest coś więcej ...
bartgol

Odpowiedzi:

59

AFAIK, który został zmieniony w C ++ 0x.

Myślę, że to tylko przeoczenie (biorąc pod uwagę, że zawsze można uzyskać częściowy efekt specjalizacji za pomocą bardziej rozwlekłego kodu, umieszczając funkcję jako staticczłonek klasy).

Możesz poszukać odpowiedniego DR (raport defektu), jeśli taki istnieje.

EDYCJA : sprawdzając to, stwierdzam, że inni też w to uwierzyli, ale nikt nie jest w stanie znaleźć takiego wsparcia w projekcie normy. Ten wątek SO wydaje się wskazywać, że częściowa specjalizacja szablonów funkcji nie jest obsługiwana w C ++ 0x .

EDYCJA 2 : tylko przykład tego, co miałem na myśli przez „umieszczanie funkcji jako staticczłonka klasy”:

#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
}
Pozdrawiam i hth. - Alf
źródło
czy masz standard w n3225? Zrobiłem szybkie wyszukiwanie, ale nie mogłem go znaleźć: /
Matthieu M.,
1
ach przepraszam ... brakowało słowa. Mam dokument, ale nie mogłem znaleźć konkretnego akapitu . Chociaż biorąc pod uwagę twoją edycję, wydaje mi się, że po prostu go tam nie ma :)
Matthieu M.,
3
Nie jest to zmieniane w C ++ 0x. Wątpię też w jego użyteczność. Zawsze możesz przeciążyć szablon i skorzystać z częściowego zamówienia .
Johannes Schaub - litb
1
Późna aktualizacja: osiem lat później nie zmieniła się nawet w C ++ 17 i nie wydaje się, aby przechodziła do C ++ 20. Nie widzę jednak powodu ...
Aconcagua
Jest to zdecydowanie najbardziej kompleksowa realizacja tej koncepcji, jak sądzę
Victor
18

Cóż, naprawdę nie możesz zrobić częściowej specjalizacji funkcji / metod, ale możesz wykonać przeciążenie.

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

Taka jest droga, ale nie wiem, czy cię to satysfakcjonuje.

Michał W.
źródło
1
Wow, mój umysł był wypełniony takimi szablonami, że naprawdę zapomniałem, jak proste mogą być rzeczy :)
Johannes
1
Niestety tak nie jest, gdy chcesz przekazać argumenty
wariadyczne
Nie jestem pewien, co oznaczało przekazywanie wariadycznych szablonów, więc chciałbym wiedzieć, czym różni się to od częściowej specjalizacji. Czy mógłbyś podać więcej szczegółów?
beginpluses
A co, jeśli potrzebujesz tylko dwóch funkcji dla wszystkich typów całkowitych i zmiennoprzecinkowych?
Dmitriy Dokshin
14

Generalnie nie zaleca się w ogóle specjalizowania szablonów funkcji ze względu na problemy z przeciążeniem. Oto dobry artykuł z dziennika użytkowników C / C ++: http://www.gotw.ca/publications/mill17.htm

I zawiera uczciwą odpowiedź na Twoje pytanie:

Po pierwsze, nie możesz ich częściowo specjalizować - po prostu dlatego, że język mówi, że nie możesz.

Georgy Pashkov
źródło
3
Artykuł nie dotyczy częściowej specjalizacji poza tym, że jest o tym wspomniany raz.
Euri Pinhollow
11

Ponieważ możesz częściowo specjalizować się w klasach, możesz użyć funktora:

#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 ;
}
Kay F. Jahnke
źródło
1
Następnie możesz użyć pojedynczego szablonu funkcji do wykonywania wywołań, pozbywając się brzydkiej ()()składni.
tmr232