136 votes

Valeur par défaut d'un paramètre lors du passage par référence en C++.

Est-il possible de donner une valeur par défaut à un paramètre d'une fonction alors que l'on passe le paramètre par référence ? en C++.

Par exemple, lorsque j'essaie de déclarer une fonction comme :

virtual const ULONG Write(ULONG &State = 0, bool sequence = true);

Lorsque je fais cela, il y a une erreur :

error C2440 : 'default argument' : impossible de convertir 'const int' en 'unsigned long &'. Une référence qui n'est pas à 'const' ne peut pas être liée à une non-valeur.

121voto

Vous pouvez le faire pour une référence constante, mais pas pour une référence non constante. En effet, le C++ ne permet pas de lier une valeur temporaire (la valeur par défaut dans ce cas) à une référence non constante.

Une façon de contourner ce problème serait d'utiliser une instance réelle comme valeur par défaut :

static int AVAL = 1;

void f( int & x = AVAL ) {
   // stuff
} 

int main() {
     f();       // equivalent to f(AVAL);
}

mais cela n'a qu'une utilité pratique très limitée.

39voto

Richard Corden Points 12292

Cela a déjà été dit dans l'un des commentaires directs à votre réponse, mais juste pour le dire officiellement. Ce que vous voulez utiliser est une surcharge :

virtual const ULONG Write(ULONG &State, bool sequence);
inline const ULONG Write()
{
  ULONG state;
  bool sequence = true;
  Write (state, sequence);
}

L'utilisation de surcharges de fonctions présente également des avantages supplémentaires. Tout d'abord, vous pouvez utiliser l'argument par défaut que vous souhaitez :

class A {}; 
class B {}; 
class C {};

void foo (A const &, B const &, C const &);
void foo (B const &, C const &); // A defaulted
void foo (A const &, C const &); // B defaulted
void foo (C const &); // A & B defaulted etc...

Il est également possible de redéfinir les arguments par défaut des fonctions virtuelles dans les classes dérivées, ce que la surcharge permet d'éviter :

class Base {
public:
  virtual void f1 (int i = 0);  // default '0'

  virtual void f2 (int);
  inline void f2 () {
    f2(0);                      // equivalent to default of '0'
  }
};

class Derived : public Base{
public:
  virtual void f1 (int i = 10);  // default '10'

  using Base::f2;
  virtual void f2 (int);
};

void bar ()
{
  Derived d;
  Base & b (d);
  d.f1 ();   // '10' used
  b.f1 ();   // '0' used

  d.f2 ();   // f1(int) called with '0' 
  b.f2 ();   // f1(int) called with '0
}

Il n'y a qu'une seule situation où une valeur par défaut doit vraiment être utilisée, et c'est sur un constructeur. Il n'est pas possible d'appeler un constructeur depuis un autre, et cette technique ne fonctionne donc pas dans ce cas.

29voto

Il existe toujours l'ancienne méthode C pour fournir des arguments optionnels : un pointeur qui peut être NULL lorsqu'il n'est pas présent :

void write( int *optional = 0 ) {
    if (optional) *optional = 5;
}

11voto

PhoenixX_2 Points 1471

Ce petit modèle vous aidera :

template<typename T> class ByRef {
public:
    ByRef() {
    }

    ByRef(const T value) : mValue(value) {
    }

    operator T&() const {
        return((T&)mValue);
    }

private:
    T mValue;
};

Alors vous pourrez le faire :

virtual const ULONG Write(ULONG &State = ByRef<ULONG>(0), bool sequence = true);

8voto

Max Lybbert Points 11822

Il y a deux raisons de passer un argument par référence : (1) pour des raisons de performance (dans ce cas, vous voulez passer par référence constante) et (2) parce que vous avez besoin de la possibilité de changer la valeur de l'argument à l'intérieur de la fonction.

Je doute fort que le passage d'un long non signé sur les architectures modernes vous ralentisse trop. Donc je suppose que vous avez l'intention de changer la valeur de State à l'intérieur de la méthode. Le compilateur se plaint parce que la constante 0 ne peut pas être modifié, car il s'agit d'une rvalue ("non-lvalue" dans le message d'erreur) et non modifiable ( const dans le message d'erreur).

En termes simples, vous voulez une méthode qui peut modifier l'argument passé, mais par défaut, vous voulez passer un argument qui ne peut pas changer.

Pour le dire autrement, le non const Les références doivent se référer à des variables réelles. La valeur par défaut dans la signature de la fonction ( 0 ) n'est pas une variable réelle. Vous rencontrez le même problème que :

struct Foo {
    virtual ULONG Write(ULONG& State, bool sequence = true);
};

Foo f;
ULONG s = 5;
f.Write(s); // perfectly OK, because s is a real variable
f.Write(0); // compiler error, 0 is not a real variable
            // if the value of 0 were changed in the function,
            // I would have no way to refer to the new value

Si vous n'avez pas l'intention de changer State à l'intérieur de la méthode, vous pouvez simplement le changer en un const ULONG& . Mais vous n'obtiendrez pas un grand avantage en termes de performances, je vous recommande donc de le remplacer par une non-référence. ULONG . Je remarque que vous renvoyez déjà un ULONG et j'ai la nette impression que sa valeur est celle de State après toutes les modifications nécessaires. Dans ce cas, je déclarerai simplement la méthode comme telle :

// returns value of State
virtual ULONG Write(ULONG State = 0, bool sequence = true);

Bien sûr, je ne sais pas trop ce que vous écrivez ni à qui. Mais c'est une autre question pour une autre fois.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X