Argomenti modello predefiniti per modelli funzione


187

Perché gli argomenti del modello predefinito sono consentiti solo sui modelli di classe? Perché non possiamo definire un tipo predefinito in un modello di funzione membro? Per esempio:

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

Al contrario, C ++ impone che gli argomenti modello predefiniti siano consentiti solo su un modello di classe.


8
+1 Questa è davvero una domanda difficile.
AraK,

1
Per le prime tre risposte pubblicate, considera questo esempio: struct S { template <class R = int> R get_me_R() { return R(); } };il parametro template non può essere dedotto dal contesto.
AraK

3
Buona domanda. 3 persone hanno già risposto dicendo che "non ha senso", e hanno tutti torto in generale. I parametri del modello di funzione non sono sempre deducibili dai parametri di chiamata della funzione. Ad esempio, se fossero autorizzati potrei scrivere template <int N = 1> int &increment(int &i) { i += N; return i; }, e poi increment(i);o increment<2>(i);. Così com'è, devo scrivere increment<1>(i);.
Steve Jessop,

In realtà, i miei e gli esempi di AraK possono essere entrambi gestiti da un sovraccarico. credo che i litb non possano, perché il parametro template potrebbe essere dedotto o potrebbe essere specificato.
Steve Jessop,

3
@Steve: il punto e virgola mancante è in realtà un nuovo sovraccarico per operatore EOL a complemento di "Sovraccarico di spazi bianchi C ++" di B. Stavtrup pubblicato nel Journal of Object-Oriented Programming, 1 aprile 1992. ( www2.research.att.com/~bs/ papers.html )

Risposte:


148

Ha senso fornire argomenti di modello predefiniti. Ad esempio, è possibile creare una funzione di ordinamento:

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

C ++ 0x li introduce in C ++. Vedi questo rapporto sui difetti di Bjarne Stroustrup: Argomenti modello predefinito per modelli funzione e cosa dice

Il divieto di argomenti di modello predefiniti per i modelli di funzione è un residuo errato del tempo in cui le funzioni indipendenti sono state trattate come cittadini di seconda classe e hanno richiesto che tutti gli argomenti del modello fossero dedotti dagli argomenti della funzione anziché specificati.

La restrizione restringe gravemente lo stile di programmazione rendendo inutilmente diverse le funzioni autoportanti dalle funzioni membro, rendendo così più difficile la scrittura di codice in stile STL.


@Arman, il collegamento alla segnalazione dei difetti contiene le modifiche apportate alla bozza di lavoro per C ++ 0x e le discussioni. Argomenti né dedotti né esplicitamente specificati sono ottenuti da argomenti predefiniti. GCC4.4 supporta argomenti predefiniti per i modelli di funzione in modalità C ++ 0x.
Johannes Schaub - litb

4
Niente a che fare con la domanda o la risposta, ma Herb Sutter ha chiamato lo standard in crescita C ++ 11 dopo gli ultimi incontri del sabato. L'ho appena letto oggi e ho voglia di condividere :) herbsutter.wordpress.com/2010/03/13/…
David Rodríguez - dribeas

e la domanda di follow-up obbligatoria ... quando ci si aspetta che arrivi ad altri compilatori :)
Jamie Cook,

@ JohannesSchaub-litb Ho avuto lo stesso problema: nessuna possibilità di specificare il tipo predefinito in una funzione modello. Ho risolto un'istanza esplicita della funzione sul tipo predefinito ( doublenel mio caso). Forse non è "generale", ma c'è qualche inconveniente con questa pratica? Grazie.
JackOLantern,

Il seguente codice non riesce a compilare, con errori come error: invalid conversion from ‘int’ to ‘int*’, qualsiasi idea del perché: `#include <array> #include <algorithm> #include <functional> template <typename Iterator, typename Comp = std :: less <Iterator>> void my_sort ( Iteratore beg, Iterator end, Comp c = Comp ()) {std :: sort (beg, end, c); } int main () {std :: array <int, 5> ar {5,2,21,7,4}; my_sort (ar.begin (), ar.end ()); } `
Luke Peterson il

36

Per citare i modelli C ++: La guida completa (pagina 207):

Quando i modelli venivano originariamente aggiunti al linguaggio C ++, gli argomenti espliciti dei modelli di funzione non erano un costrutto valido. Gli argomenti del modello di funzione dovevano sempre essere deducibili dall'espressione della chiamata. Di conseguenza, non sembra esistere alcun motivo valido per consentire gli argomenti del modello di funzione predefinito poiché il valore predefinito verrà sempre ignorato dal valore dedotto.


semplice e conciso :)
InQusitive il

17

Finora, tutti gli esempi offerti di parametri di modello predefiniti per i modelli di funzione possono essere eseguiti con sovraccarichi.

ARAK:

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

potrebbe essere:

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

Il mio:

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

potrebbe essere:

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())

potrebbe essere:

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

Potrebbe essere:

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

Che ho dimostrato con il seguente codice:

#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)
}

L'output stampato corrisponde ai commenti di ciascuna chiamata a f e la chiamata commentata non viene compilata come previsto.

Quindi sospetto che i parametri del modello predefinito "non siano necessari", ma probabilmente solo nello stesso senso in cui gli argomenti della funzione predefinita "non sono necessari". Come indica il rapporto sui difetti di Stroustrup, l'aggiunta di parametri non dedotti era troppo tardi perché qualcuno potesse rendersi conto e / o apprezzare davvero che rendeva utili le impostazioni predefinite. Quindi la situazione attuale è in effetti basata su una versione di modelli di funzioni che non è mai stata standard.


@Steve: Quindi l'uovo correva più veloce del pollo? :) interessante. Grazie.
Arman,

1
Probabilmente solo una di quelle cose. Il processo di standardizzazione C ++ è in parte lento in modo che le persone abbiano il tempo di rendersi conto quando un cambiamento crea opportunità o difficoltà altrove nello standard. Si spera che le persone incontrino difficoltà nell'implementare il progetto di standard man mano che vanno avanti, quando individuano una contraddizione o un'ambiguità. Le opportunità per consentire cose che prima non erano consentite, si basano su qualcuno che vuole scrivere il codice notando che non deve più essere illegale ...
Steve Jessop

2
Uno di più per voi: template<typename T = void> int SomeFunction();. Il parametro template qui non viene mai usato, e infatti la funzione non viene mai chiamata; l'unico posto a cui si fa riferimento è in a decltypeo sizeof. Il nome corrisponde deliberatamente al nome di un'altra funzione, ma il fatto che sia un modello indica che il compilatore preferirà la funzione libera se esiste. I due vengono utilizzati in SFINAE per fornire un comportamento predefinito in cui manca una definizione di funzione.
Tom,

4

Su Windows, con tutte le versioni di Visual Studio è possibile convertire questo errore ( C4519 ) in un avviso o disabilitarlo in questo modo:

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

Vedi maggiori dettagli qui .


1
Si noti che, sebbene ciò disabiliti il ​​messaggio "Gli argomenti di modello predefiniti sono consentiti solo su un modello di classe", in realtà non fa in modo che il processo di istanza del modello usi il valore fornito. Ciò richiede VS2013 (o qualsiasi altro compilatore che abbia completato il difetto C ++ 11 226 "Argomenti modello predefiniti per modelli funzione")
puetzk

1

Quello che uso è il prossimo trucco:

Diciamo che vuoi avere una funzione come questa:

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

Non ti sarà permesso, ma lo farò nel modo seguente:

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

Quindi in questo modo puoi usarlo in questo modo:

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

Come possiamo vedere, non è necessario impostare esplicitamente il secondo parametro. Forse sarà utile per qualcuno.


Questa non è sicuramente una risposta.
Cucciolo

@deadmg - puoi spiegare perché? Non siamo tutti guru del modello C ++. Grazie.
Kev

Questa è una soluzione alternativa che è piuttosto ordinata ma non copre tutti i casi che potresti desiderare. Ad esempio, come lo applicheresti a un costruttore?
Tiberiu Savin,

@TiberiuSavin - se ti ho capito bene, allora puoi fare così: template <typename E, typename ARR_E> worker <E, ARR_E> :: worker (ARR_E * parr) {parr_ = parr; }. E quindi usalo in questo modo: worker <int> w2 (& my_array);
Alariq,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.