web-dev-qa-db-fra.com

Valeur par défaut pour un paramètre en passant par référence en C++

Est-il possible de donner une valeur par défaut à un paramètre d'une fonction pendant que nous passons le paramètre par référence. en C++

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

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

Quand je fais cela, cela donne une erreur:

erreur C2440: 'argument par défaut': 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

106
Sony

Vous pouvez le faire pour une référence const, mais pas pour une référence non const. Cela est dû au fait que C++ n'autorise pas l'attribution d'un temporaire (la valeur par défaut dans ce cas) à une référence non const.

Une solution consiste à utiliser une instance réelle par défaut:

static int AVAL = 1;

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

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

mais son utilisation pratique est très limitée.

87
anon

Cela a déjà été dit dans l'un des commentaires directs de votre réponse, mais il suffit de le déclarer 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 choisir n'importe quel argument par défaut:

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 une classe dérivée, ce qui évite les surcharges:

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 existe une seule situation dans laquelle un paramètre par défaut doit vraiment être utilisé, à savoir un constructeur. Il n'est pas possible d'appeler un constructeur depuis un autre et cette technique ne fonctionne donc pas dans ce cas.

27
Richard Corden

L'ancien moyen de fournir des arguments optionnels est toujours le suivant: un pointeur pouvant être NULL lorsqu'il n'est pas présent:

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

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

Ensuite, vous pourrez:

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

Il existe deux raisons de passer un argument par référence: (1) pour les performances (auquel cas vous souhaitez passer par référence const) et (2) car vous devez pouvoir modifier la valeur de l'argument dans la fonction.

Je doute fort que le fait de passer un long moment sans signature sur des architectures modernes vous ralentisse trop. Donc, je suppose que vous avez l'intention de changer la valeur de State dans la méthode. Le compilateur se plaint parce que la constante 0 ne peut pas être modifiée car il s'agit d'une valeur 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 puisse changer l'argument passé, mais vous voulez par défaut passer un argument qui ne peut pas changer.

En d'autres termes, les références non -const doivent faire référence à 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 réellement l'intention de changer State dans la méthode, vous pouvez simplement le changer en const ULONG&. Mais vous n'allez pas en tirer de gros avantages en termes de performances. Je vous recommande donc de le remplacer par une variable ULONG non référencée. Je remarque que vous retournez déjà un ULONG et je soupçonne sournois que sa valeur est la valeur de State après les modifications nécessaires. Dans ce cas, je déclarerais 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 où. Mais c'est une autre question pour une autre fois.

6
Max Lybbert

Vous ne pouvez pas utiliser un littéral constant pour un paramètre par défaut pour la même raison que vous ne pouvez pas en utiliser un en tant que paramètre de l'appel de fonction. Les valeurs de référence doivent avoir une adresse, pas les valeurs de références constantes (c'est-à-dire qu'elles peuvent être des valeurs r ou des littéraux constants).

int* foo (int& i )
{
   return &i;
}

foo(0); // compiler error.

const int* bar ( const int& i )
{
   return &i;
}

bar(0); // ok.

Assurez-vous que votre valeur par défaut a une adresse et tout va bien.

int null_object = 0;

int Write(int &state = null_object, bool sequence = true)
{
   if( &state == &null_object )
   {
      // called with default paramter
      return sequence? 1: Rand();
   }
   else
   {
      // called with user parameter
      state += sequence? 1: Rand();
      return state;
   }
}

J'ai utilisé ce modèle à quelques reprises où j'avais un paramètre qui pourrait être une variable ou null. L’approche habituelle consiste à faire passer l’utilisateur dans un pointeur, c’est le cas. Ils transmettent un pointeur NULL s'ils ne veulent pas que vous remplissiez la valeur. J'aime nuls l'approche objet. Cela facilite la vie des appelants sans compliquer terriblement le code de l'appelé.

5
deft_code

Non, ce n'est pas possible.

Passer par référence implique que la fonction peut changer la valeur du paramètre. Si le paramètre n'est pas fourni par l'appelant et provient de la constante par défaut, quelle est la fonction supposée changer?

4
NascarEd

C'est possible avec le qualificatif const pour State:

virtual const ULONG Write(const ULONG &State = 0, bool sequence = true);
1
vic
void f(const double& v = *(double*) NULL)
{
  if (&v == NULL)
    cout << "default" << endl;
  else
    cout << "other " << v << endl;
}
1
Waxrat

Je ne le pense pas. La raison en est que les valeurs par défaut sont évaluées en constantes et que les valeurs passées par référence doivent pouvoir changer, à moins que vous ne le déclariez également comme référence constante.

1
ilya n.

Une autre façon pourrait être la suivante:

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

// wrapper
const ULONG Write(bool sequence = true)
{
   ULONG dummy;
   return Write(dummy, sequence);
}

alors les appels suivants sont possibles:

ULONG State;
object->Write(State, false); // sequence is false, "returns" State
object->Write(State); // assumes sequence = true, "returns" State
object->Write(false); // sequence is false, no "return"
object->Write(); // assumes sequence = true, no "return"
1
JJTh

Dans le cas de OO ... Dire qu'une classe donnée a et "Par défaut" signifie que cette valeur par défaut (valeur) doit être déclarée avec acondingly puis peut être USD comme paramètre par défaut

class Pagination {
public:
    int currentPage;
    //...
    Pagination() {
        currentPage = 1;
        //...
    }
    // your Default Pagination
    static Pagination& Default() {
        static Pagination pag;
        return pag;
    }
};

Sur votre méthode ...

 shared_ptr<vector<Auditoria> > 
 findByFilter(Auditoria& audit, Pagination& pagination = Pagination::Default() ) {

Cette solution est tout à fait appropriée car dans ce cas, la "pagination globale par défaut" est une valeur "de référence" unique. Vous aurez également le pouvoir de modifier les valeurs par défaut au moment de l'exécution, comme une configuration "au niveau mondial" ex: préférences de navigation de la pagination des utilisateurs, etc.

1
wdavilaneto

J'ai une solution de contournement pour cela, voir l'exemple suivant sur la valeur par défaut pour int&:

class Helper
{
public:
    int x;
    operator int&() { return x; }
};

// How to use it:
void foo(int &x = Helper())
{

}

Vous pouvez le faire pour tout type de données trivial, tel que bool, double ...

0
Omar Natour

Il y a aussi un truc plutôt sale pour ça:

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

Dans ce cas, vous devez l'appeler avec std::move:

ULONG val = 0;
Write(std::move(val));

Ce n'est que quelques solutions de contournement amusantes, je ne recommande absolument pas son utilisation dans du code réel!

0
avtomaton
void revealSelection(const ScrollAlignment& = ScrollAlignment::alignCenterIfNeeded, bool revealExtent = false);
0
Abhijeet Kandalkar