2016-03-17 10 views
14

Hier ist mein Code:Ist es möglich, den Memberfunktionsaufruf als Standardargument zu verwenden?

struct S 
{ 
    int f() { return 1; } 
    int g(int arg = f()) { return arg; } 
}; 

int main() 
{ 
    S s; 
    return s.g(); 
} 

Dies schlägt mit dem Fehler zu kompilieren:

error: cannot call member function 'int S::f()' without object 

Der Versuch this->f() funktioniert auch nicht, wie this nicht in diesem Zusammenhang verwendet werden können.

Gibt es eine Möglichkeit, dies zu tun, immer noch mit dem Standardargument?


Natürlich kann es um Argumente nicht mit dem Standardüberhaupt gearbeitet werden:

int g(int arg) { return arg; } 
int g() { return g(f()); } 

jedoch, dass die ausführlichen bekommt man bedenkt, dass in dem „realen Code“ gibt es mehr Parameter vor arg, und mehrere Funktionen folgen diesem Muster. (Und noch hässlicher, wenn es in der einen Funktion mehrere Standardargumente gab).

NB. This question sieht zunächst ähnlich aus, aber tatsächlich fragt er, wie man eine Schließung bildet, was ein anderes Problem ist (und die verknüpfte Lösung trifft nicht auf meine Situation zu).

+0

Es sollte sein: int g() {return g (f()); } ', oder? Zumindest im echten Code würde es vielleicht viel Sinn ergeben. Natürlich, hier funktioniert es für Sie nur die 'return' Anweisung in' g'. – skypjack

+0

@skypjack danke, behoben –

Antwort

12

Sie können nur Mitglieder verwenden, wenn sie static sind. Aus einem Entwurf von C++ 11 (n3299), §8.3.6/9:

z.B. dies funktioniert:

struct S { 
    static int f() { return 1; } 
    int g(int arg = f()) { return arg; } 
}; 

int main() 
{ 
    S s; 
    return s.g(); 
} 

Dies funktioniert auch (ich glaube, das ist, was der erste Ausdruck bedeutet):

struct S { 
    int f() { return 42; } 
    int g(int arg); 
}; 

static S global; 

int S::g(int arg = global.f()) { return arg; } 

int main() 
{ 
    S s; 
    return s.g(); 
} 

Was this, ist es in der Tat nicht erlaubt (§8.3.6/8):

Die default arguments Seite auf cppreference.com hat viele Details bezüglich des Subjet - es kann sehr komplex werden.

+1

"Das Schlüsselwort' this' darf nicht in einem Standardargument einer Elementfunktion verwendet werden. " – Jaege

+0

Danke @Jaege, fügte dieses Zitat auch hinzu. – Mat

5

Wenn Sie experimentelle Funktionen von C++ 17 verwenden dürfen, können Sie std::optional von der STL verwenden (siehe here für weitere Details).

Mit anderen Worten so etwas wie:

int g(std::optional<int> oarg = std::optional<int>{}) { 
    int arg = oarg ? *oarg : f(); 
    // go further 
} 

EDIT

Wie in den Kommentaren vorgeschlagen, über den Code logisch äquivalent unter dem einen sein sollte:

int g(std::optional<int> oarg = std::optional<int>{}) { 
    int arg = oarg.value_or(f()); 
    // go further 
} 

Diese man ist etwas lesbarer (oder?), aber bitte beachten Sie, dass es in jedem Fall f ausführt.
Wenn diese Funktion teuer ist, lohnt es sich vielleicht nicht.

+0

Hoppla, in meinem echten Code wird das Argument nicht durch den Wert, sondern durch die const-Referenz genommen. Ich wusste nicht, dass das zuerst einen Unterschied machen würde. Ich denke, dass Ihre Lösung dennoch gelten würde (kann den umschlossenen Typ zu 'reference_wrapper' oder etwas ändern) –

+0

@ M.M Ja, es gilt immer noch. Wie auch immer, ich habe eine weitere Antwort hinzugefügt, die vielleicht eine noch bessere Lösung bietet (zumindest einen Punkt, von dem aus ich anfangen kann). Lassen Sie mich wissen, ob es für Sie geeignet ist. – skypjack

+0

Nur ein kleiner Vorschlag - ich denke, dass 'value_or' anstelle von'?: 'In solchen Fällen verwendet werden sollte. – Predelnik

3

Ich füge eine weitere Antwort hinzu, die sich komplett von der vorherigen unterscheidet und Ihr Problem lösen könnte.
Die Idee ist, eine andere Klasse und die richtige Mischung aus expliziten und nicht expliziten Konstruktoren zu verwenden.
Es folgt eine minimale, Arbeitsbeispiel:

#include <functional> 
#include <iostream> 

template<class C, int(C::*M)()> 
struct Arg { 
    std::function<int(C*)> fn; 
    Arg(int i): fn{[i](C*){ return i; }} { } 
    explicit Arg(): fn{[](C* c){ return (c->*M)(); }} { } 
}; 

struct S { 
    int f() { return 1; } 
    int h() { return 2; } 
    void g(int arg0, 
      Arg<S, &S::f> arg1 = Arg<S, &S::f>{}, 
      Arg<S, &S::h> arg2 = Arg<S, &S::h>{}) 
    { 
     std::cout << "arguments" << std::endl; 
     std::cout << "arg0: " << arg0 << std::endl; 
     std::cout << "arg1: " << arg1.fn(this) << std::endl; 
     std::cout << "arg2: " << arg2.fn(this) << std::endl; 
    } 
}; 

int main() { 
    S s{}; 
    s.g(42, 41, 40); 
    s.g(0); 
} 

Das Beispiel zeigt, wie Sie beide Standardparameter und nicht notleidenden diejenigen mischen.
Es ist ziemlich einfach, es zu ändern und g eine Funktion mit einer leeren Argumentliste zu sein, wie in der ursprünglichen Frage.
Ich bin mir auch ziemlich sicher, dass man das Beispiel verfeinern und mit etwas Besserem enden kann, trotzdem sollte es ein guter Ausgangspunkt sein.

Es folgt die Lösung des ursprünglichen Beispiel von der Frage angewandt:

#include <functional> 

template<class C, int(C::*M)()> 
struct Arg { 
    std::function<int(C*)> fn; 
    Arg(int i): fn{[i](C*){ return i; }} { } 
    explicit Arg(): fn{[](C* c){ return (c->*M)(); }} { } 
}; 

struct S { 
    int f() { return 1; } 
    int g(Arg<S, &S::f> arg = Arg<S, &S::f>{}) { 
     return arg.fn(this); 
    } 
}; 

int main() { 
    S s{}; 
    return s.g(); 
} 

Und das ist alles, es möglich ist, das zu tun, auch ohne static Methoden oder globale Variablen.
Natürlich können wir unsere diese irgendwie verwenden. Es geht darum, die Sprache etwas zu verbiegen ...

Verwandte Themen