是否可以使用成员函数调用作为默认参数?

M.M*_*M.M 16 c++ member-functions default-arguments

这是我的代码:

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

int main()
{
    S s;
    return s.g();
}
Run Code Online (Sandbox Code Playgroud)

这无法编译错误:

error: cannot call member function 'int S::f()' without object
Run Code Online (Sandbox Code Playgroud)

尝试this->f()也不起作用,因为this可能不会在该上下文中使用.

有没有办法使这个工作仍然使用默认参数?


当然可以通过不使用默认参数来解决它:

int g(int arg) { return arg; }
int g() { return g(f()); }
Run Code Online (Sandbox Code Playgroud)

然而,考虑到在"真实代码"之前有更多参数arg,以及遵循这种模式的几个函数,这会变得冗长.(如果在一个函数中有多个默认参数,那就更难看了).

NB.这个问题起初看起来很相似,但实际上他问的是如何形成一个闭包,这是一个不同的问题(并且链接的解决方案不适用于我的情况).

Mat*_*Mat 15

你只能在那里使用会员static.从C++ 11草案(n3299),§8.3.6/ 9:

类似地,非静态成员不应在默认参数中使用,即使它未被计算,除非它作为类成员访问表达式(5.2.5)的id表达式出现,或者除非用于形成指向成员的指针(5.3.1).

例如,这工作:

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

int main()
{
  S s;
  return s.g();
}
Run Code Online (Sandbox Code Playgroud)

这也有效(我认为这是第一个表达式的含义):

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();
}
Run Code Online (Sandbox Code Playgroud)

至于this,确实不允许(§8.3.6/ 8):

关键字this不得用于成员函数的默认参数.

cppreference.com上的默认参数页面有很多关于子喷射的详细信息 - 它可能变得非常复杂.


sky*_*ack 5

如果允许您使用C++ 17中的实验性功能,则可以使用std::optionalSTL(有关详细信息,请参见此处).

在其他方面类似于:

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

编辑

正如评论中所建议的那样,上面的代码在逻辑上应该等同于下面的代码:

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

这个有点可读(不是吗?),但请注意它f在任何情况下都会执行.
如果这个功能很昂贵,也许它不值得.


sky*_*ack 5

我添加了另一个答案,它与上一个完全不同,可以解决您的问题。
这个想法是使用另一个类以及显式和非显式构造函数的正确组合。
它遵循一个最小的工作示例:

#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);
}
Run Code Online (Sandbox Code Playgroud)

该示例显示了如何混合默认参数和非默认参数。
修改它非常简单,让它g成为一个具有空参数列表的函数,就像在原始问题中一样。
我也很确定可以改进这个例子并以比这更好的东西结束,无论如何这应该是一个很好的起点。

它遵循应用于问题中原始示例的解决方案:

#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();
}
Run Code Online (Sandbox Code Playgroud)

就是这样,即使没有static方法或全局变量,也可以做到这一点。
当然,我们可以以某种方式使用我们的this。这是稍微弯曲语言的问题......