Argomenti predefiniti del modello per i modelli di funzione

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

struct mycclass { template void mymember(T* vec) { // ... } }; 

Al contrario, C ++ forza che gli argomenti del modello predefiniti sono consentiti solo su un modello di class.

Ha senso dare argomenti template predefiniti. Ad esempio potresti creare una funzione di ordinamento:

 template::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 predefiniti del modello per i modelli di funzione e cosa dice

La proibizione degli argomenti modello predefiniti per i modelli di funzione è un residuo errato del tempo in cui le funzioni indipendenti venivano trattate come cittadini di seconda class e richiedeva che tutti gli argomenti del modello fossero dedotti dagli argomenti della funzione anziché specificati.

La restrizione ostacola seriamente lo stile di programmazione rendendo inutilmente funzioni freestanding diverse dalle funzioni membro, rendendo quindi più difficile scrivere codice in stile STL.

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

Quando i modelli venivano inizialmente aggiunti al linguaggio C ++, gli argomenti del modello di funzione esplicita non erano un costrutto valido. Gli argomenti del modello di funzione dovevano sempre essere deducibili dall’espressione di chiamata. Di conseguenza, non sembrava esserci alcun motivo convincente per consentire gli argomenti dei modelli di funzione predefiniti, poiché l’impostazione predefinita sarebbe sempre sostituita dal valore dedotto.

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

ARAK:

 struct S { template  R get_me_R() { return R(); } }; 

potrebbe essere:

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

Il mio:

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

potrebbe essere:

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

litb:

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

potrebbe essere:

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

Stroustrup:

 template  void f(T t = 0, U u = 0); 

Potrebbe essere:

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

Che ho dimostrato con il seguente codice:

 #include  #include  #include  #include  template  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  void g(S s, T t){ std::cout << "f<" << typeid(S).name() << "," << typeid(T).name() << ">(" << s << "," << prettify(t) << ")\n"; } template  void f(S s = 0, T t = 0){ g(s,t); } template  void f(S s = 0, double t = 0) { g(s, t); } int main() { f(1, 'c'); // f(1,'c') f(1); // f(1,0) // f(); // error: T cannot be deduced f(); // f(0,0) f(); // f(0,0) } 

L’output stampato corrisponde ai commenti per ogni chiamata a f e la chiamata commentata non riesce a compilare come previsto.

Quindi sospetto che i parametri predefiniti del modello “non siano necessari”, ma probabilmente solo nello stesso senso in cui gli argomenti delle funzioni predefinite “non sono necessari”. Come indica il rapporto sui difetti di Stroustrup, l’aggiunta di parametri non dedotti era troppo tardi per consentire a chiunque di realizzare e / o davvero apprezzare che rendesse le impostazioni predefinite utili. Quindi la situazione attuale è in effetti basata su una versione di modelli di funzione che non è mai stata standard.

Su Windows, con tutte le versioni di Visual Studio è ansible convertire questo errore ( C4519 ) in un avviso o distriggersrlo 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 .

Quello che uso è il trucco successivo:

Diciamo che vuoi avere una funzione come questa:

 template  > void doStuff(ARR_E array) { E one(1); array.add( one ); } 

Non ti sarà permesso, ma faccio il prossimo passo:

 template  struct MyArray_t { void add(T i) { // ... } }; template  > class worker { public: /*static - as you wish */ ARR_E* parr_; void doStuff(); /* do not make this one static also, MSVC complains */ }; template  void worker::doStuff() { E one(1); parr_->add( one ); } 

Quindi in questo modo puoi usarlo in questo modo:

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

Come possiamo vedere non è necessario impostare in modo esplicito il secondo parametro. Forse sarà utile per qualcuno.