Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to use member function call as default argument?

Here is my code:

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

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

This fails to compile with the error:

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

Trying this->f() doesn't work either, as this may not be used in that context.

Is there a way to make this work, still using the default argument?


Of course it can be worked around by not using default arguments at all:

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

however that gets verbose considering that in the "real code" there are more parameters before arg, and several functions following this pattern. (And even more ugly if there were multiple default arguments in the one function).

NB. This question looks similar at first, but in fact he is asking how to form a closure, which is a different problem (and the linked solution doesn't apply to my situation).

like image 215
M.M Avatar asked Mar 17 '16 06:03

M.M


2 Answers

You can only use members there if they are static. From a C++11 draft standard (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).

E.g., this works:

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

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

This also works (I think that's what the first expression means):

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

As for this, it is indeed not allowed (§8.3.6/8):

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

The default arguments page on cppreference.com has a lot of details regarding the subject—it can get quite complex.

like image 200
Mat Avatar answered Oct 20 '22 22:10

Mat


I add another answer, that is completely different from the previous one and could solve your issue.
The idea is to use another class and the right mix of explicit and non-explicit constructors.
It follows a minimal, working example:

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

The example shows how you can mix both default parameters and non defaulted ones.
It's quite simple to modify it and let g be a function having an empty argument list, as in the original question.
I'm also quite sure that one can refine the example and end with something better than that, anyway it should be a good point from which to start.

It follows the solution applied to the original example from the question:

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

And that's all, it's possible to do that, even without static methods or global variables.
Of course, we can use our this somehow. It's a matter of bending the language a bit...

like image 31
skypjack Avatar answered Oct 20 '22 21:10

skypjack