web-dev-qa-db-fra.com

Arguments de modèle par défaut pour les modèles de fonction

Pourquoi les arguments de modèle par défaut sont-ils autorisés uniquement sur les modèles de classe? Pourquoi ne pouvons-nous pas définir un type par défaut dans un modèle de fonction membre? Par exemple:

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

Au lieu de cela, C++ force que les arguments de modèle par défaut ne sont autorisés que sur un modèle de classe.

182
Arman

Il est logique de donner des arguments de modèle par défaut. Par exemple, vous pouvez créer une fonction de tri:

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 les introduit à C++. Voir ce rapport de défaut de Bjarne Stroustrup: Arguments de modèle par défaut pour les modèles de fonction et ce qu’il dit

L'interdiction des arguments de modèle par défaut pour les modèles de fonction est un vestige erroné de l'époque où les fonctions indépendantes étaient traitées comme des citoyens de deuxième classe et nécessitaient que tous les arguments de modèle soient déduits des arguments de la fonction plutôt que spécifiés.

La restriction restreint gravement le style de programmation en rendant inutilement différentes fonctions indépendantes des fonctions membres, rendant ainsi plus difficile l’écriture de code de style STL.

145

Pour citer Modèles C++: Le Guide complet (page 207):

Lors de l'ajout initial de modèles au langage C++, les arguments de modèle de fonction explicites n'étaient pas une construction valide. Les arguments de modèle de fonction devaient toujours être déductibles de l'expression d'appel. En conséquence, il semble n'y avoir aucune raison impérieuse d'autoriser les arguments de modèle de fonction par défaut, car ceux-ci seraient toujours remplacés par la valeur déduite.

35
James McNellis

Jusqu'à présent, tous les exemples proposés de paramètres de modèle par défaut pour les modèles de fonction peuvent être réalisés avec des surcharges.

AraK:

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

pourrait être:

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

Le mien:

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

pourrait être:

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

pourrait être:

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

Pourrait être:

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

Ce que j'ai prouvé avec le code suivant:

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

La sortie imprimée correspond aux commentaires pour chaque appel à f, et l'appel commenté ne réussit pas à se compiler comme prévu.

Je suppose donc que les paramètres de modèle par défaut "ne sont pas nécessaires", mais probablement uniquement dans le même sens que les arguments de fonction par défaut "ne sont pas nécessaires". Comme l'indique le rapport de défaut de Stroustrup, l'ajout de paramètres non déduits était trop tardif pour permettre à quiconque de se rendre compte et/ou de réellement comprendre que les défauts par défaut étaient utiles. La situation actuelle est donc basée sur une version de modèles de fonctions qui n’a jamais été standard.

17
Steve Jessop

Sous Windows, avec toutes les versions de Visual Studio, vous pouvez convertir cette erreur ( C4519 ) en un avertissement ou la désactiver de la manière suivante:

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

Voir plus de détails ici .

4
Adi Shavit

Ce que j'utilise est le prochain truc:

Disons que vous voulez avoir une fonction comme ceci:

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

Vous ne serez pas autorisé, mais je le fais de la manière suivante:

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

Donc, de cette façon, vous pouvez l'utiliser comme ceci:

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

Comme nous pouvons le voir, il n'est pas nécessaire de définir explicitement le deuxième paramètre. Peut-être que ce sera utile pour quelqu'un.

1
alariq