如何从非静态成员函数创建函数对象
我正在尝试创建一个函数对象,该对象映射到类的非静态成员函数。下面是我想要做的事情:
#include <functional>
template<typename T, T v>
struct MyClass
{
T internal_state;
T foo()
{
T x = v;
internal_state = v;
return x;
}
};
int main()
{
MyClass<int, 20> obj;
std::function f0 = obj.foo; // Try 1
std::function f1 = std::bind(obj.foo, &obj, std::placeholders::_1); // Try 2
std::function f2 = std::bind(decltype(obj)::foo, &obj, std::placeholders::_1); // Try 3
std::function f3 = std::bind(&decltype(obj)::foo, &obj, std::placeholders::_1); // Try 4
return f();
}
我想创建一个函数对象,映射到 MyClass 的 foo 函数。我需要这个函数不是静态函数。我尝试使用 std::function 来创建一个函数对象,然后我试图把 foo 赋给它。我尝试了这4 种不同的方法,但没有一个起作用。你可以在这里找到代码 这里 on compiler explorer.
当我的代码中只有 f0 时(其余部分被注释掉),编译器说我在使用 obj 的非静态成员函数。为了解决这个问题,我转向 f1,它将 this 指针绑定到 obj.foo,理论上应该让函数成为静态的,但我仍然得到相同的错误。然后我想也许我绑定函数的方式不对,于是我改用 f2。不过,我再次得到同样的错误。在网上搜索了一阵后,我找到了显式使用 & 运算符来获取函数地址的代码片段,这把我带到了 f3。然而,即使使用 f3,我仍然在模板推导失败方面得到编译错误:
In file included from <source>:1:
/cefs/e2/e24e2d6b522c5f622e3fd471_gcc-trunk-20260520/include/c++/17.0.0/functional: In instantiation of 'struct std::_Bind_check_arity<int (MyClass<int, 20>::*)(), MyClass<int, 20>*, const std::_Placeholder<1>&>':
/cefs/e2/e24e2d6b522c5f622e3fd471_gcc-trunk-20260520/include/c++/17.0.0/functional:954:12: required from 'struct std::_Bind_helper<false, int (MyClass<int, 20>::*)(), MyClass<int, 20>*, const std::_Placeholder<1>&>'
954 | struct _Bind_helper
| ^~~~~~~~~~~~
/cefs/e2/e24e2d6b522c5f622e3fd471_gcc-trunk-20260520/include/c++/17.0.0/functional:976:5: required by substitution of 'template<class _Func, class ... _BoundArgs> typename std::_Bind_helper<std::__is_socketlike<_Func>::value, _Func, _BoundArgs ...>::type std::bind(_Func&&, _BoundArgs&& ...) [with _Func = int (MyClass<int, 20>::*)(); _BoundArgs = {MyClass<int, 20>*, const std::_Placeholder<1>&}]'
976 | bind(_Func&& __f, _BoundArgs&&... __args)
| ^~~~
<source>:21:33: required from here
21 | std::function f3 = std::bind(&decltype(obj)::foo, &obj, std::placeholders::_1); // Try 4
| ~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/cefs/e2/e24e2d6b522c5f622e3fd471_gcc-trunk-20260520/include/c++/17.0.0/functional:942:21: error: static assertion failed: Wrong number of arguments for pointer-to-member
941 | static_assert(_Varargs::value
| ~~~~~
942 | ? sizeof...(_BoundArgs) >= _Arity::value + 1
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
943 | : sizeof...(_BoundArgs) == _Arity::value + 1,
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
• '(false ? (2 >= (((long unsigned int)std::integral_constant<long unsigned int, 0>::value) + 1)) : (2 == (((long unsigned int)std::integral_constant<long unsigned int, 0>::value) + 1)))' evaluates to false
<source>: In function 'int main()':
<source>:21:82: error: class template argument deduction failed:
21 | std::function f3 = std::bind(&decltype(obj)::foo, &obj, std::placeholders::_1); // Try 4
| ^
<source>:21:82: error: no matching function for call to 'function(std::_Bind_helper<false, int (MyClass<int, 20>::*)(), MyClass<int, 20>*, const std::_Placeholder<1>&>::type)'
• there are 4 candidates
In file included from /cefs/e2/e24e2d6b522c5f622e3fd471_gcc-trunk-20260520/include/c++/17.0.0/functional:76:
• candidate 1: 'template<class _Signature> function() -> std::function<_Signature>'
/cefs/e2/e24e2d6b522c5f622e3fd471_gcc-trunk-20260520/include/c++/17.0.0/bits/std_function.h:113:11:
113 | class function;
| ^~~~~~~~
• candidate expects 0 arguments, 1 provided
• candidate 2: 'template<class _Signature> function(std::function<_Signature>) -> std::function<_Signature>'
• template argument deduction/substitution failed:
• 'std::_Bind<int (MyClass<int, 20>::*(MyClass<int, 20>*, std::_Placeholder<1>))()>' is not derived from 'std::function<_Signature>'
<source>:21:82:
21 | std::function f3 = std::bind(&decltype(obj)::foo, &obj, std::placeholders::_1); // Try 4
| ^
• candidate 3: 'template<class _Fn, class _Signature> std::function(_Fn) -> function<_Signature>'
/cefs/e2/e24e2d6b522c5f622e3fd471_gcc-trunk-20260520/include/c++/17.0.0/bits/std_function.h:721:5:
721 | function(_Fn) -> function<_Signature>;
| ^~~~~~~~
• template argument deduction/substitution failed:
• error: 'decltype' cannot resolve address of overloaded function
/cefs/e2/e24e2d6b522c5f622e3fd471_gcc-trunk-20260520/include/c++/17.0.0/bits/std_function.h:719:26:
719 | template<typename _Fn, typename _Signature
| ^~~~~~~~
• candidate 4: 'template<class _Res, class ... _ArgTypes> std::function(_Res (*)(_ArgTypes ...)) -> function<_Res(_ArgTypes ...)>'
/cefs/e2/e24e2d6b522c5f622e3fd471_gcc-trunk-20260520/include/c++/17.0.0/bits/std_function.h:717:5:
717 | function(_Res(*)(_ArgTypes...)) -> function<_Res(_ArgTypes...)>;
| ^~~~~~~~
• template argument deduction/substitution failed:
• mismatched types '_Res (*)(_ArgTypes ...)' and 'std::_Bind<int (MyClass<int, 20>::*(MyClass<int, 20>*, std::_Placeholder<1>))()>'
<source>:21:82:
21 | std::function f3 = std::bind(&decltype(obj)::foo, &obj, std::placeholders::_1); // Try 4
| ^
<source>:22:12: error: 'f' was not declared in this scope
22 | return f();
| ^
Compiler returned: 1
我到底哪里做错了?我该如何创建一个映射到 obj.foo 的函数对象?
解决方案
正如评论中所提到的,std::placeholders::_1 是用于你没有绑定、从结果可调用对象传递给包装函数的函数参数。但 MyClass<int,20>::foo 在绑定 &obj 后就没有额外的参数了。你不需要它。但...
bind 返回一个有点奇怪的辅助类型。你可以用得到的函数对象传入比实际使用的参数更多的参数。这些多出的参数会被简单地忽略。举例:
MyClass<int, 20> obj;
auto f0 = std::bind(&MyClass<int,20>::foo,&obj);
f0(); // OK
f0(42); // OK!
f0(42,42); // OK!
因此,你不能从从 std::bind 返回的可调用对象中推导出 std::function 的模板参数,以得到 std::function<int()>。
这按预期工作:
int main()
{
MyClass<int, 20> obj;
std::function<int()> f0 = std::bind(&MyClass<int,20>::foo,&obj);
return f0();
}
通过lambda绑定参数更简单:
std::function f0 = [&obj](){ return obj.foo(); };
也许你甚至不需要 std::function,可以直接使用lambda。如果以后你想在 f0 中存储不同类型的可调用对象,你需要 std::function 进行类型擦除:
std::function f0 = [&obj](){ return obj.foo(); };
f0 = std::bind(&MyClass<int,20>::foo,&obj);
如果不是,简单地这样做:
auto f0 = [&obj](){ return obj.foo(); };
auto f1 = std::bind(&MyClass<int,20>::foo,&obj);
附注:你也有一个尝试 // Try 1,其实并没有真正尝试把对象绑定到成员函数上,我认为值得一提的是,将成员函数包装成一个函数对象也很简单:
MyClass<int, 20> obj;
std::function<int(MyClass<int,20>&)> f0{&MyClass<int,20>::foo};
return f0(obj);
std::function 具有一些工具,可以轻松将成员函数指针转换为一个接收实例引用的可调用对象。对于 std::function<int(MyClass<int,20>*)>,它对接收对象指针的情况也同样适用。