2016-03-17 14 views
14

Oto mój kod:Czy można użyć wywołania funkcji member jako domyślnego argumentu?

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

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

To nie skompilować z błędem:

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

Próbując this->f() nie działa albo, jak this nie mogą być używane w tym kontekście.

Czy istnieje sposób, aby to zadziałało, nadal używając domyślnego argumentu?


Oczywiście można to obejść w ogóle nie używając domyślnych argumentów:

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

jednak, że dostaje gadatliwy biorąc pod uwagę, że w „prawdziwym kodem” istnieje więcej parametrów przed arg, a kilka funkcje zgodne z tym wzorcem. (I jeszcze bardziej brzydki, jeśli w jednej funkcji było wiele domyślnych argumentów).

NB. This question na początku wygląda podobnie, ale w rzeczywistości pyta, jak utworzyć zamknięcie, co jest innym problemem (a połączone rozwiązanie nie ma zastosowania w mojej sytuacji).

+0

Powinno to być 'int g() {return g (f()); } ', prawda? Przynajmniej w prawdziwym kodzie, może miałoby to sens. Oczywiście, tutaj działa tak, że masz tylko instrukcję 'return' w' g'. – skypjack

+0

@skypjack thanks, fixed –

Odpowiedz

12

Możesz używać tylko tych członków, jeśli są static. Z wersji C++ 11 (n3299), §8.3.6/9:

Similarly, a non-static member shall not be used in a default argument, even if it is not evaluated, unless it appears as the id-expression of a class member access expression (5.2.5) or unless it is used to form a pointer to member (5.3.1).

np. to działa:

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

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

Działa to również (myślę, że to, co oznacza pierwsze wyrażenie):

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

chodzi o this, to rzeczywiście jest niedozwolone (§8.3.6/8):

The keyword this shall not be used in a default argument of a member function.

Strona na stronie cppreference.com zawiera wiele szczegółów dotyczących subjet - może być dość skomplikowana.

+1

"Słowo kluczowe" this "nie będzie używane w domyślnym argumencie funkcji składowej." – Jaege

+0

Dzięki @Jaege, dodał również ten cytat. – Mat

5

Jeśli możesz korzystać z funkcji eksperymentalnych z C++ 17, możesz użyć std::optional ze STL (więcej informacji znajdziesz w artykule here).

Innymi słowy coś takiego:

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

EDIT

Jak sugerowano w komentarzach, powyższy kod powinien być logicznie równoważne do poniższego:

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

ten jeden jest nieco bardziej czytelny (prawda?), ale należy pamiętać, że w każdym przypadku wykonuje on kod f.
Jeśli ta funkcja jest kosztowna, może nie jest tego warta.

+0

Ups, w moim prawdziwym kodzie argument jest brany przez odniesienie do const, a nie przez wartość. Nie zdawałem sobie sprawy, że na początku będzie to miało wpływ. Myślę, że twoje rozwiązanie nadal będzie obowiązywało (może zmienić typ zawijany na "reference_wrapper" lub coś podobnego). –

+0

@ M.M Tak, nadal obowiązuje. W każdym razie dodałem kolejną odpowiedź, która oferuje może jeszcze lepsze rozwiązanie (przynajmniej punkt, od którego należy zacząć). Daj mi znać, jeśli to odpowiednie podejście. – skypjack

+0

Tylko mała sugestia - myślę, że w takich przypadkach powinno się używać 'value_or' zamiast'?: '. – Predelnik

3

Dodaję kolejną odpowiedź, która jest zupełnie inna niż poprzednia i może rozwiązać Twój problem.
Chodzi o to, aby użyć innej klasy i odpowiedniej kombinacji konstruktorów jawnych i nie jawnych.
Wynika minimalny, działający przykład:

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

Przykład pokazuje, jak można mieszać oba parametry domyślne i są w tych niewykonaniem zobowiązania.
Jest całkiem prosta do modyfikacji i pozwala g być funkcją mającą pustą listę argumentów, tak jak w oryginalnym pytaniu.
Jestem również pewien, że można udoskonalić przykład i zakończyć z czymś lepszym niż to, w każdym razie powinien to być dobry punkt, od którego zacząć.

Wynika rozwiązanie zastosowane do pierwotnego przykład od pytania:

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

I to wszystko, jest to możliwe do zrobienia, że ​​nawet bez static metod i zmiennych globalnych.
Oczywiście, możemy w jakiś sposób użyć naszego tego. Chodzi o to, aby nieco wygiąć język ...

Powiązane problemy