Por que o template de função não pode ser parcialmente especializado?

87

Eu sei que a especificação da linguagem proíbe a especialização parcial do template de função.

Eu gostaria de saber o motivo pelo qual isso proíbe? Eles não são úteis?

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
fonte
Pois template<typename T, typename U> void f(T t, U u) {}também template<> void f(int t, char u) {}é permitido.
precipitado
10
Acho interessante que as pessoas continuem fornecendo soluções alternativas quando a questão não é "como posso atingir um objetivo semelhante", mas "qual é a razão por trás desse comportamento" ... Eu mesmo não sei o motivo dessa escolha, mas presumo que o o comitê deve ter tido um motivo para proibir a especialização parcial do modelo de função. Até agora, a explicação "mais próxima" é o link postado por Georgy, que apenas aponta os "riscos" potenciais da especialização parcial do template de função quando há sobrecargas. No entanto, não acho que seja uma razão para proibir esse recurso, então presumo que haja mais do que isso ..
bartgol

Respostas:

59

AFAIK que mudou em C ++ 0x.

Acho que foi apenas um descuido (considerando que você sempre pode obter o efeito de especialização parcial com um código mais detalhado, colocando a função como um staticmembro de uma classe).

Você pode consultar o DR (Relatório de Defeito) relevante, se houver.

EDIT : verificando isso, descobri que outros também acreditaram nisso, mas ninguém é capaz de encontrar tal suporte no projeto de norma. Este encadeamento SO parece indicar que a especialização parcial de modelos de função não é compatível com C ++ 0x .

EDIT 2 : apenas um exemplo do que quero dizer com "colocar a função como um staticmembro de uma classe":

#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
}
Saúde e hth. - Alf
fonte
você tem o padrão no n3225? Fiz uma pesquisa rápida, mas não consegui encontrar: /
Matthieu M.
1
ah desculpe ... faltou uma palavra. Tenho o documento, mas não consegui encontrar o parágrafo específico . Embora dada a sua edição, acho que é simplesmente porque não está lá :)
Matthieu M.
3
Isso não é alterado em C ++ 0x. Também duvido de sua utilidade. Você sempre pode sobrecarregar o modelo e fazer uso de pedidos parciais .
Johannes Schaub - litb
1
Atualização tardia: não mudou nem mesmo no C ++ 17, oito anos depois, e também não parece entrar no C ++ 20. Não consigo ver nenhuma razão para, entretanto ...
Aconcágua
Esta é de longe a implementação mais abrangente do conceito, eu acredito
Victor
18

Bem, você realmente não pode fazer especialização parcial de função / método, mas pode sobrecarregar.

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

É o caminho mas não sei se te satisfaz.

Michal W
fonte
1
Nossa, minha mente estava cheia de modelos que eu realmente esqueci como as coisas podiam ser simples :)
Johannes
1
Infelizmente, não é o caso quando você deseja passar argumentos variáveis ​​após especializar parcialmente a função .. :(
Gwangmu Lee
Não tenho certeza do que significava passar modelos variados, então gostaria de saber como é diferente de uma especialização parcial. Você poderia fornecer mais detalhes?
beginpluses
E se você quiser apenas duas funções para todos os tipos integrais e flutuantes?
Dmitriy Dokshin
14

Em geral, não é recomendado especializar modelos de função, devido a problemas com sobrecarga. Aqui está um bom artigo do C / C ++ Users Journal: http://www.gotw.ca/publications/mill17.htm

E contém uma resposta honesta à sua pergunta:

Por um lado, você não pode especializá-los parcialmente - basicamente porque a linguagem diz que você não pode.

Geórgui pashkov
fonte
3
O artigo não é sobre especialização parcial, além de ser mencionado uma vez.
Euri Pinhollow de
11

Como você pode especializar parcialmente as classes, pode usar um functor:

#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
fonte
1
Você pode então usar um único modelo de função para fazer as chamadas, livrando-se da ()()sintaxe feia .
tmr232