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!

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
}

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.


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.


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

1
Następnie możesz użyć pojedynczego szablonu funkcji do wykonywania wywołań, pozbywając się brzydkiej ()()składni.
tmr232
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.