Argumentos de modelo padrão para modelos de função

187

Por que argumentos de modelo padrão são permitidos apenas em modelos de classe? Por que não podemos definir um tipo padrão em um modelo de função de membro? Por exemplo:

struct mycclass {
  template<class T=int>
  void mymember(T* vec) {
    // ...
  }
};

Em vez disso, o C ++ força que os argumentos padrão do modelo sejam permitidos apenas em um modelo de classe.

Arman
fonte
8
+1 Essa é realmente uma pergunta complicada.
AraK 15/03/10
1
Para as três primeiras respostas postadas, considere este exemplo: struct S { template <class R = int> R get_me_R() { return R(); } };O parâmetro do modelo não pode ser deduzido do contexto.
AraK 15/03/10
3
Boa pergunta. 3 pessoas já responderam dizendo que "não faz sentido" e estão erradas em geral. Os parâmetros do modelo de função nem sempre são dedutíveis dos parâmetros de chamada de função. Por exemplo, se eles tivessem permissão, eu poderia escrever template <int N = 1> int &increment(int &i) { i += N; return i; }e depois increment(i);ou increment<2>(i);. Como é, eu tenho que escrever increment<1>(i);.
Steve Jessop
Na verdade, os exemplos dos meus e do AraK podem ser tratados com sobrecarga. O litb não pode, eu acho, porque o parâmetro do modelo pode ser deduzido ou especificado.
Steve Jessop
3
@Steve: O ponto-e-vírgula que falta é, na verdade, uma nova sobrecarga de operador de EOL para complementar a "Sobrecarga de espaço em branco de C ++ da B. Stavtrup" publicada no Journal of Object-Oriented Programming, 1 de abril de 1992. ( www2.research.att.com/~bs/ papers.html )

Respostas:

148

Faz sentido fornecer argumentos de modelo padrão. Por exemplo, você pode criar uma função de classificação:

template<typename Iterator, 
         typename Comp = std::less<
            typename std::iterator_traits<Iterator>::value_type> >
void sort(Iterator beg, Iterator end, Comp c = Comp()) {
  ...
}

O C ++ 0x os apresenta ao C ++. Veja este relatório de defeitos de Bjarne Stroustrup: Argumentos de modelo padrão para modelos de função e o que ele diz

A proibição de argumentos de modelo padrão para modelos de função é um remanescente errado do tempo em que as funções independentes foram tratadas como cidadãos de segunda classe e exigiram que todos os argumentos do modelo fossem deduzidos dos argumentos da função em vez de especificados.

A restrição contraria seriamente o estilo de programação, tornando desnecessariamente diferentes as funções independentes das funções de membro, dificultando a gravação de código no estilo STL.

Johannes Schaub - litb
fonte
@Arman, o link do relatório de defeitos contém as alterações feitas no rascunho de trabalho do C ++ 0x e nas discussões. Argumentos não deduzidos nem especificados explicitamente são obtidos a partir de argumentos padrão. O GCC4.4 suporta argumentos padrão para modelos de função no modo C ++ 0x.
Johannes Schaub - litb 15/03
4
Nada a ver com a pergunta ou resposta, mas Herb Sutter chamou o padrão C ++ 11, após a última reunião de sábado. Acabei de ler hoje e sinto vontade de compartilhar :) herbsutter.wordpress.com/2010/03/13/…
David Rodríguez - dribeas 15/03/10 -
eo acompanhamento obrigatório se pergunta ... Quando é que este deverá fazer a sua maneira em outros compiladores :)
Jamie Cook
@ JohannesSchaub-litb Eu tive o mesmo problema: não há possibilidade de especificar o tipo padrão em uma função de modelo. Eu resolvi por uma instanciação explícita da função no tipo padrão ( doubleno meu caso). Talvez não seja "geral", mas há alguma desvantagem nessa prática? Obrigado.
31413
O código a seguir falha na compilação, com erros como error: invalid conversion from ‘int’ to ‘int*’ , qualquer idéia do porquê: `#include <array> #include <algorithm> #include <functional> template <typename Iterator, typename Comp = std :: less <Iterator>> void my_sort ( Iterator beg, Iterator end, Comp c = Comp ()) {std :: sort (beg, end, c); } int main () {std :: array <int, 5> ar {5,2,21,7,4}; minha_sort (ar.begin (), ar.end ()); } `
Luke Peterson
36

Para citar os modelos C ++: O guia completo (página 207):

Quando os modelos foram originalmente adicionados à linguagem C ++, os argumentos explícitos do modelo de função não eram uma construção válida. Os argumentos do modelo de função sempre precisavam ser dedutíveis da expressão de chamada. Como resultado, parecia não haver motivo convincente para permitir argumentos de modelo de função padrão, porque o padrão sempre seria substituído pelo valor deduzido.

James McNellis
fonte
simples e conciso :)
InQusitive
17

Até o momento, todos os exemplos apresentados de parâmetros de modelo padrão para modelos de função podem ser feitos com sobrecargas.

AraK:

struct S { 
    template <class R = int> R get_me_R() { return R(); } 
};

poderia ser:

struct S {
    template <class R> R get_me_R() { return R(); } 
    int get_me_R() { return int(); }
};

Meu próprio:

template <int N = 1> int &increment(int &i) { i += N; return i; }

poderia ser:

template <int N> int &increment(int &i) { i += N; return i; }
int &increment(int &i) { return increment<1>(i); }

litb:

template<typename Iterator, typename Comp = std::less<Iterator> >
void sort(Iterator beg, Iterator end, Comp c = Comp())

poderia ser:

template<typename Iterator>
void sort(Iterator beg, Iterator end, std::less<Iterator> c = std::less<Iterator>())

template<typename Iterator, typename Comp >
void sort(Iterator beg, Iterator end, Comp c = Comp())

Stroustrup:

template <class T, class U = double>
void f(T t = 0, U u = 0);

Poderia ser:

template <typename S, typename T> void f(S s = 0, T t = 0);
template <typename S> void f(S s = 0, double t = 0);

O que eu provei com o seguinte código:

#include <iostream>
#include <string>
#include <sstream>
#include <ctype.h>

template <typename T> T prettify(T t) { return t; }
std::string prettify(char c) { 
    std::stringstream ss;
    if (isprint((unsigned char)c)) {
        ss << "'" << c << "'";
    } else {
        ss << (int)c;
    }
    return ss.str();
}

template <typename S, typename T> void g(S s, T t){
    std::cout << "f<" << typeid(S).name() << "," << typeid(T).name()
        << ">(" << s << "," << prettify(t) << ")\n";
}


template <typename S, typename T> void f(S s = 0, T t = 0){
    g<S,T>(s,t);
}

template <typename S> void f(S s = 0, double t = 0) {
    g<S,double>(s, t);
}

int main() {
        f(1, 'c');         // f<int,char>(1,'c')
        f(1);              // f<int,double>(1,0)
//        f();               // error: T cannot be deduced
        f<int>();          // f<int,double>(0,0)
        f<int,char>();     // f<int,char>(0,0)
}

A saída impressa corresponde aos comentários de cada chamada para f e a chamada comentada falha ao compilar conforme o esperado.

Portanto, suspeito que os parâmetros padrão do modelo "não são necessários", mas provavelmente apenas no mesmo sentido em que argumentos de função padrão "não são necessários". Como o relatório de defeitos da Stroustrup indica, a adição de parâmetros não deduzidos era tarde demais para alguém perceber e / ou realmente apreciar que isso tornava os padrões úteis. Portanto, a situação atual é baseada em uma versão dos modelos de função que nunca foi padrão.

Steve Jessop
fonte
@ Steve: Então o ovo estava correndo mais rápido que o frango? :) interessante. Obrigado.
Arman
1
Provavelmente apenas uma dessas coisas. O processo de padronização do C ++ é lento, em parte, para que as pessoas tenham tempo para perceber quando uma mudança cria oportunidades ou dificuldades em outras partes do padrão. Esperamos que as dificuldades sejam percebidas pelas pessoas que implementam o rascunho do padrão à medida que avançam, quando identificam uma contradição ou ambiguidade. Oportunidades para permitir coisas que antes não eram permitidas, dependem de alguém que queira escrever o código, notando que ele não precisa mais ser ilegal ... #
1760 Steve
2
Um mais para você: template<typename T = void> int SomeFunction();. O parâmetro do modelo aqui nunca é usado e, de fato, a função nunca é chamada; o único local a que se refere é um decltypeou sizeof. O nome corresponde deliberadamente ao nome de outra função, mas o fato de ser um modelo significa que o compilador preferirá a função livre, se existir. Os dois são usados ​​no SFINAE para fornecer comportamento padrão onde uma definição de função está ausente.
Tom
4

No Windows, com todas as versões do Visual Studio, você pode converter esse erro ( C4519 ) em um aviso ou desabilitá-lo da seguinte forma:

#ifdef  _MSC_VER
#pragma warning(1 : 4519) // convert error C4519 to warning
// #pragma warning(disable : 4519) // disable error C4519
#endif

Veja mais detalhes aqui .

Adi Shavit
fonte
1
Observe que, embora isso desative a mensagem "argumentos padrão do modelo são permitidos apenas em um modelo de classe", na verdade, ele não faz o processo de instanciação do modelo usar o valor fornecido. Isso requer VS2013 (ou qualquer outro compilador que completou ++ 11 defeito 226 "argumentos modelo padrão para modelos de função" c)
puetzk
1

O que eu uso é o próximo truque:

Vamos dizer que você deseja ter uma função como esta:

template <typename E, typename ARR_E = MyArray_t<E> > void doStuff(ARR_E array)
{
    E one(1);
    array.add( one );
}

Você não será permitido, mas eu faço da próxima maneira:

template <typename T>
struct MyArray_t {
void add(T i) 
{
    // ...
}
};

template <typename E, typename ARR_E = MyArray_t<E> >
class worker {
public:
    /*static - as you wish */ ARR_E* parr_;
    void doStuff(); /* do not make this one static also, MSVC complains */
};

template <typename E, typename ARR_E>
void worker<E, ARR_E>::doStuff()
{
    E one(1);
    parr_->add( one );
}

Portanto, você pode usá-lo desta maneira:

MyArray_t<int> my_array;
worker<int> w;
w.parr_ = &arr;
w.doStuff();

Como podemos ver, não é necessário definir explicitamente o segundo parâmetro. Talvez seja útil para alguém.

alariq
fonte
Definitivamente, isso não é uma resposta.
Filhote de cachorro
@deadmg - você pode explicar o porquê? Não somos todos gurus de modelos C ++. Obrigado.
Kev
Esta é uma solução alternativa bastante interessante, mas não cobre todos os casos que você deseja. Por exemplo, como você aplicaria isso a um construtor?
Tiberiu Savin
@ TibiuSavin - se eu entendi direito, você pode fazer assim: template <nome do tipo E, nome do tipo ARR_E> trabalhador <E, ARR_E> :: trabalhador (ARR_E * parr) {parr_ = parr; } E então use-o assim: worker <int> w2 (& my_array);
Alariq