如何从非静态成员函数创建函数对象

编程语言 2026-07-09

我正在尝试创建一个函数对象,该对象映射到类的非静态成员函数。下面是我想要做的事情:

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

我想创建一个函数对象,映射到 MyClassfoo 函数。我需要这个函数不是静态函数。我尝试使用 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>*)>,它对接收对象指针的情况也同样适用。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章