是否可以使用成员函数调用作为默认参数?
Is it possible to use member function call as default argument?
这是我的代码:
struct S
{
int f() { return 1; }
int g(int arg = f()) { return arg; }
};
int main()
{
S s;
return s.g();
}
编译失败并出现错误:
error: cannot call member function 'int S::f()' without object
尝试 this->f()
也不起作用,因为 this
可能不会在该上下文中使用。
有没有办法让它工作,仍然使用默认参数?
当然可以通过根本不使用默认参数来解决这个问题:
int g(int arg) { return arg; }
int g() { return g(f()); }
然而,考虑到在 "real code" 中 arg
之前有更多参数,并且有几个函数遵循这种模式,所以这变得冗长。 (如果一个函数中有多个默认参数,那就更丑了)。
注意。 This question乍一看很像,但实际上他是在问如何形成闭包,这是一个不同的问题(并且链接的解决方案不适用于我的情况)。
您只能在那里使用 static
的成员。来自 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).
例如,这个有效:
struct S {
static int f() { return 1; }
int g(int arg = f()) { return arg; }
};
int main()
{
S s;
return s.g();
}
这也有效(我认为这就是第一个表达式的意思):
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();
}
至于this
,确实是不允许的(§8.3.6/8):
The keyword this
shall not be used in a default argument of a member function.
cppreference.com 上的 default arguments 页面有很多关于该主题的详细信息 — 它可能会变得非常复杂。
如果允许您使用 C++17 中的实验性功能,则可以使用 STL 中的 std::optional
(有关详细信息,请参阅 here)。
换句话说:
int g(std::optional<int> oarg = std::optional<int>{}) {
int arg = oarg ? *oarg : f();
// go further
}
编辑
如评论中所建议,上面的代码在逻辑上应该等同于下面的代码:
int g(std::optional<int> oarg = std::optional<int>{}) {
int arg = oarg.value_or(f());
// go further
}
这个更易读(不是吗?),但请注意它在任何情况下都会执行 f
。
如果那个功能很贵,也许不值得。
我添加另一个答案,它与上一个完全不同,可以解决您的问题。
这个想法是使用另一个 class 以及显式和非显式构造函数的正确组合。
它遵循一个最小的工作示例:
#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);
}
该示例显示了如何混合使用默认参数和非默认参数。
修改它并让 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();
}
仅此而已,即使没有 static
方法或全局变量,也可以做到这一点。
当然,我们可以以某种方式使用我们的this。这是一个稍微弯曲语言的问题...
这是我的代码:
struct S
{
int f() { return 1; }
int g(int arg = f()) { return arg; }
};
int main()
{
S s;
return s.g();
}
编译失败并出现错误:
error: cannot call member function 'int S::f()' without object
尝试 this->f()
也不起作用,因为 this
可能不会在该上下文中使用。
有没有办法让它工作,仍然使用默认参数?
当然可以通过根本不使用默认参数来解决这个问题:
int g(int arg) { return arg; }
int g() { return g(f()); }
然而,考虑到在 "real code" 中 arg
之前有更多参数,并且有几个函数遵循这种模式,所以这变得冗长。 (如果一个函数中有多个默认参数,那就更丑了)。
注意。 This question乍一看很像,但实际上他是在问如何形成闭包,这是一个不同的问题(并且链接的解决方案不适用于我的情况)。
您只能在那里使用 static
的成员。来自 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).
例如,这个有效:
struct S {
static int f() { return 1; }
int g(int arg = f()) { return arg; }
};
int main()
{
S s;
return s.g();
}
这也有效(我认为这就是第一个表达式的意思):
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();
}
至于this
,确实是不允许的(§8.3.6/8):
The keyword
this
shall not be used in a default argument of a member function.
cppreference.com 上的 default arguments 页面有很多关于该主题的详细信息 — 它可能会变得非常复杂。
如果允许您使用 C++17 中的实验性功能,则可以使用 STL 中的 std::optional
(有关详细信息,请参阅 here)。
换句话说:
int g(std::optional<int> oarg = std::optional<int>{}) {
int arg = oarg ? *oarg : f();
// go further
}
编辑
如评论中所建议,上面的代码在逻辑上应该等同于下面的代码:
int g(std::optional<int> oarg = std::optional<int>{}) {
int arg = oarg.value_or(f());
// go further
}
这个更易读(不是吗?),但请注意它在任何情况下都会执行 f
。
如果那个功能很贵,也许不值得。
我添加另一个答案,它与上一个完全不同,可以解决您的问题。
这个想法是使用另一个 class 以及显式和非显式构造函数的正确组合。
它遵循一个最小的工作示例:
#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);
}
该示例显示了如何混合使用默认参数和非默认参数。
修改它并让 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();
}
仅此而已,即使没有 static
方法或全局变量,也可以做到这一点。
当然,我们可以以某种方式使用我们的this。这是一个稍微弯曲语言的问题...