wake-up-neo.com

Standardwert für einen Parameter bei der Referenzübergabe in C++

Ist es möglich, einem Parameter einer Funktion einen Standardwert zuzuweisen, während der Parameter per Referenz übergeben wird. in C++

Zum Beispiel, wenn ich versuche, eine Funktion zu deklarieren:

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

Wenn ich das tue, gibt es einen Fehler:

fehler C2440: 'Standardargument': Umwandlung von 'const int' in 'unsigned long &' .__ nicht möglich. Eine Referenz, die nicht auf 'const' steht, kann nicht an einen Nicht-l-Wert gebunden werden

106
Sony

Sie können dies für eine const-Referenz tun, nicht jedoch für eine nicht-const-Referenz. Dies liegt daran, dass C++ nicht zulässt, dass ein temporärer Wert (der Standardwert in diesem Fall) an einen nicht konstanten Verweis gebunden wird.

Eine Möglichkeit wäre, eine tatsächliche Instanz als Standard zu verwenden:

static int AVAL = 1;

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

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

dies ist jedoch von sehr begrenztem praktischen Nutzen.

87
anon

Es wurde bereits in einem direkten Kommentar zu Ihrer Antwort gesagt, aber nur um es offiziell zu sagen. Was Sie verwenden möchten, ist eine Überladung:

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

Die Verwendung von Funktionsüberlastungen bietet auch zusätzliche Vorteile. Erstens können Sie jedes Argument vorgeben, das Sie möchten:

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...

Es ist auch möglich, Standardargumente für virtuelle Funktionen in abgeleiteten Klassen neu zu definieren, wodurch eine Überladung vermieden wird:

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
}

Es gibt nur eine Situation, in der wirklich ein Standardwert verwendet werden muss, und zwar in einem Konstruktor. Es ist nicht möglich, einen Konstruktor von einem anderen aus aufzurufen. Daher funktioniert diese Technik in diesem Fall nicht.

27
Richard Corden

Es gibt immer noch den alten Weg, optionale Argumente bereitzustellen: Ein Zeiger, der NULL sein kann, wenn er nicht vorhanden ist

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

Diese kleine Vorlage wird Ihnen helfen:

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

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

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

private:
    T mValue;
};

Dann können Sie:

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

Es gibt zwei Gründe, ein Argument als Referenz zu übergeben: (1) für die Leistung (in diesem Fall möchten Sie die Konstantenreferenz übergeben) und (2), weil Sie die Möglichkeit haben müssen, den Wert des Arguments in der Funktion zu ändern.

Ich bezweifle stark, dass es Ihnen zu viel abbremst, wenn Sie an modernen Architekturen lange Zeit nicht unterzeichnet haben. Ich gehe also davon aus, dass Sie beabsichtigen, den Wert von State innerhalb der Methode zu ändern. Der Compiler beschwert sich, da die Konstante 0 nicht geändert werden kann, da es sich um einen r-Wert ("non-lvalue" in der Fehlermeldung) und nicht veränderbar (const in der Fehlernachricht) handelt.

Einfach gesagt, Sie möchten eine Methode, die das übergebene Argument ändern kann. Standardmäßig möchten Sie jedoch ein Argument übergeben, das nicht geändert werden kann.

Anders ausgedrückt: Nicht -const-Referenzen müssen sich auf tatsächliche Variablen beziehen. Der Standardwert in der Funktionssignatur (0) ist keine echte Variable. Sie haben das gleiche Problem wie:

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

Wenn Sie State innerhalb der Methode nicht wirklich ändern möchten, können Sie es einfach in einen const ULONG& ändern. Sie werden jedoch keinen großen Leistungsvorteil davon erhalten, daher würde ich empfehlen, es in eine Nicht-Referenz ULONG zu ändern. Ich stelle fest, dass Sie bereits ein ULONG zurückgeben, und ich habe den hinterlistigen Verdacht, dass sein Wert nach allen erforderlichen Änderungen der Wert von State ist. In diesem Fall würde ich die Methode einfach so deklarieren:

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

Natürlich bin ich nicht ganz sicher, was Sie schreiben oder wohin. Aber das ist eine andere Frage für ein anderes Mal.

6
Max Lybbert

Sie können kein Konstantenliteral für einen Standardparameter verwenden. Aus demselben Grund können Sie nicht als Parameter für den Funktionsaufruf verwendet werden. Referenzwerte müssen eine Adresse haben, konstante Referenzwerte müssen nicht sein (dh sie können r-Werte oder konstante Literale sein).

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

foo(0); // compiler error.

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

bar(0); // ok.

Stellen Sie sicher, dass Ihr Standardwert über eine Adresse verfügt und Sie in Ordnung sind.

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

Ich habe dieses Muster ein paar Mal verwendet, bei dem ich einen Parameter hatte, der eine Variable oder Null sein konnte. Der reguläre Ansatz besteht darin, dass der Benutzer einen Zeiger übergeben muss. Dies ist der Fall. Sie übergeben einen NULL-Zeiger, wenn Sie nicht möchten, dass Sie den Wert eingeben. Ich mag Null-Objekt-Ansatz. Es erleichtert dem Anrufer das Leben, ohne den Code der Aufregung zu erschweren.

5
deft_code

Nein, es ist nicht möglich.

Das Übergeben von Verweisen impliziert, dass die Funktion möglicherweise den Wert des Parameters ändert. Wenn der Parameter nicht vom Aufrufer bereitgestellt wird und von der Standardkonstante stammt, was soll sich die Funktion ändern?

4
NascarEd

Mit const Qualifier für State ist es möglich:

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

Ich denke nicht, und der Grund ist, dass Standardwerte in Konstanten ausgewertet werden und Werte, die durch Verweis übergeben werden, geändert werden müssen, es sei denn, Sie deklarieren ihn auch als konstanten Verweis.

1
ilya n.

Ein anderer Weg könnte der folgende sein:

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

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

dann sind folgende anrufe möglich:

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

Im Falle von OO ... Zu sagen, dass eine gegebene Klasse und "Default" hat, bedeutet, dass dieser Default (Wert) entsprechend deklariert werden muss und dann als Standardparameter usd verwendet werden kann.

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

Auf deiner Methode ...

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

Diese Lösung ist gut geeignet, da in diesem Fall "Global default Pagination" ein einzelner "Referenzwert" ist. Sie haben auch die Möglichkeit, Standardwerte zur Laufzeit zu ändern, z. B. eine Konfiguration auf "globaler Ebene", z.

1
wdavilaneto

Ich habe eine Problemumgehung, siehe das folgende Beispiel für den Standardwert für int&:

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

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

}

Sie können dies für jeden beliebigen trivialen Datentyp tun, z. B. bool, double ...

0
Omar Natour

Dafür gibt es auch einen schmutzigen Trick:

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

In diesem Fall müssen Sie es mit std::move aufrufen:

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

Es ist nur ein lustiger Workaround, ich empfehle es nicht in echtem Code!

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