绑定通用成员函数

2024-04-03

有时我需要将一些成员函数绑定到其调用对象,以相同的同构方式对待成员函数和非成员函数。例如(典型的回调示例):

#include <vector>
#include <functional>

void f(int){}

struct foo
{
    void f(int){}
};

int main()
{
    using namespace std::placeholders;

    foo my_foo;
    std::vector<std::function<void()>> functions;

    functions.push_back( f );
    functions.push_back([](int){});
    functions.push_back( std::bind( &foo::f , my_foo , _1 ) );


    for( auto& function : functions )
    {
        function(0);
    }
}

随着成员函数的参数越多,我们需要在函数中放入更多的占位符std::bind() call.

现在考虑它的通用版本。应该不会有问题吧?:

#include <vector>
#include <functional>

void f(int){}

struct foo
{
    void f(int){}
};

template<typename FIRST , typename SECOND , typename THIRD>
class callback_list
{
    using callback_t = std::function<void(FIRST,SECOND,THIRD>)>;



    //Overload for non-member handlers:
    void add( const callback_t& handler )
    {
        _handlers.push_back( handler );
    }

    //Overload for member handlers:
    template<typename CLASS>
    void add( CLASS& object_ref , 
                      void(CLASS::*member_function)( FIRST,SECOND,THIRD ) )
    {
        using namespace std::placeholders;

        _handlers.push_back( std::bind( member_function , 
                                        std::ref( object_ref ) , 
                                        _1 , _2 , _3
                                      ) 
                           );
    }

    template<typename... ARGS>
    void operator()( ARGS&&... args )
    {
        for( auto& handler : _handlers )
            handler( std::forward<ARGS>( args )... );
    } 

private:
    std::vector<callback_t> functions;
};


void f(int,int,int){}

struct foo
{
    void f(int,int,int){}
};

int main()
{
    using namespace std::placeholders;

    foo my_foo;
    callback_list<int,int,int> callbacks;

    callbacks.add( f );
    callbacks.add([](int,int,int){});
    callbacks.add( my_foo , &foo::f );

    callbacks(0,0,0);
}

Ok. The add()成员回调的重载只是将对象绑定到成员函数,并且因为回调具有三个参数,所以我们使用三个占位符。

但请考虑一下:如果回调有任意数量的参数怎么办?.
换句话说,如果callback_list类模板是用可变参数模板定义的?:

template<typename... ARGS>
class callback_list{ ... };

如何将可变参数函数与已知的任何函数参数绑定std::bind()调用,即使用未指定数量的占位符?


跟...共事std::bind,我们需要以某种方式提供一定数量的占位符,具体取决于回调函数参数的数量。我已经描述了一种方法here https://stackoverflow.com/a/21193316/420683如何通过创建占位符生成器来做到这一点:

template<int> struct placeholder_template {};

通过部分专业化std::is_placeholder对于上面的模板,std::bind看到实例化placeholder_template<N>作为占位符类型。使用通常的索引技巧,我们然后展开placeholder_template<0>{}, placeholder<1>{}, ...., placeholder<N-1>{}, where N是函数参数的数量。

template<typename... Params>
class callback_list
{
public:
    using callback_t = std::function<void(Params...)>;

    //Overload for non-member handlers:
    void add( const callback_t& handler )
    {
        _handlers.push_back( handler );
    }

private:
    //Overload for member handlers:
    template<typename CLASS, int... Is>
    void add( CLASS& object_ref , 
              void(CLASS::*member_function)( Params... ) ,
              int_sequence<Is...> )
    {
        using namespace std::placeholders;

        _handlers.push_back( std::bind( member_function , 
                                        std::ref( object_ref ) , 
                                        placeholder_template<Is>{}...
                                      ) 
                           );
    }
public:
    template<typename CLASS>
    void add( CLASS& object_ref , 
              void(CLASS::*member_function)( Params... ) )
    {
        add( object_ref, member_function,
             make_int_sequence<sizeof...(Params)>{} );
    }
    

    template<typename... ARGS>
    void operator()( ARGS&&... args )
    {
        for( auto& handler : _handlers )
            handler( std::forward<ARGS>( args )... );
    } 

private:
    std::vector<callback_t> _handlers;
};

占位符生成器和整数序列的代码,来自另一个答案:

template<int...> struct int_sequence {};

template<int N, int... Is> struct make_int_sequence
    : make_int_sequence<N-1, N-1, Is...> {};
template<int... Is> struct make_int_sequence<0, Is...>
    : int_sequence<Is...> {};

template<int> // begin with 0 here!
struct placeholder_template
{};

#include <functional>
#include <type_traits>

namespace std
{
    template<int N>
    struct is_placeholder< placeholder_template<N> >
        : integral_constant<int, N+1> // the one is important
    {};
}

使用C++14,可以使用std::index_sequence_for<Params...>而不是定制make_int_sequence<sizeof...(Params)>.


旁注:如果您想接受带有 cv- 和 ref-限定符的成员函数,您可以使用非常通用的“模式”,例如

template<class C, class T>
void add(C& ref, T fun);

并通过 SFINAE 进行限制。这里有一个特点 http://coliru.stacked-crooked.com/a/00750bf7564ab6d4它可以让你从这样的函数指针中推断出参数的数量(通过tuple_size).

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

绑定通用成员函数 的相关文章

  • C# 中的简单获取字符串(忽略末尾的数字)

    我认为正则表达式太过杀伤力 而且它需要我一些时间来编写一些代码 我想我现在应该学习 因为我知道一些正则表达式 分隔字母数字字符串中的字符串的最简单方法是什么 它将永远是 LLLLDDDDD 我只想要字母 l 通常只有 1 或 2 个字母 T
  • Microsoft Graph API 授权错误:无效受众

    我知道这是一个很长的问题 但如果有人能与我分享他们的想法或经验 我真的很感激 因为我已经解决这个问题几天了 现在正在尝试很多事情 我有一个 ASP Net Core 3 1 Web API 应用程序和一个 ASP NET Core 3 1
  • return 语句是否为按值返回的函数创建临时对象?

    当我学习 C 11 右值引用和移动语义时 我开始对函数如何返回值来初始化变量感到困惑 看下面的例子 Widget makeWidget Widget w return w Widget w1 makeWidget 这里我假设没有 RVO 即
  • F1 2019 UDP解码

    我目前正在为 F1 方向盘开发自己的显示器 F1 2019 由codemasters提供 通过UDP发送数据 该数据存储在字节数组中 我在解码返回的数组时遇到一些问题 问题是我得到了很多信息 但我不知道如何处理它们 我将向您介绍我所尝试过的
  • Linux 缓冲区溢出环境变量

    我一直在审查不同类型的缓冲区溢出 并遇到了一个我不记得为什么会发生的问题 下面的代码是我尝试执行缓冲区溢出的程序 include
  • 如何设置属性选择器的值 Expression>

    我需要使用模式工厂的想法将 Person 类实体中的实体属性 Address 与 FactoryEntities 类中的表达式 linq 相关联 看看这就是我所拥有的并且我想要做的 Address address new Address a
  • 提高 ASP.NET/C# 编译速度的最佳方法是什么?

    更新 请将您的答案集中在硬件解决方案上 您使用什么硬件 工具 插件来提高 ASP NET 编译和首次执行速度 我们正在寻找固态硬盘来加快速度 但现在价格确实很高 我现在有两个 RAID 0 的 7200 rpm 硬盘 但我对性能不再满意 所
  • 这个对象的内存会是什么样子?

    我想知道这个类 它的对象 的内存布局是什么样的 class MyClass string myString int myInt public MyClass string str int i myString str myInt i MyC
  • C++ 中的静态虚函数

    我有一个基类和一个派生类 我想更改基函数 同时保持它们静态 因为它们应该作为静态传递给其他函数 我怎样才能做到这一点 ATL 框架通过将基类设为模板 然后让派生类将其类类型作为模板参数传递 从而绕过了无虚拟静态的限制 然后 基类可以在需要时
  • 委托:方法名称预期错误

    我正在尝试让以下简单的委托示例正常工作 根据我从中取出的一本书 应该没问题 但我得到了Method name expected error namespace TestConsoleApp class Program private del
  • Windows Phone 8.1 应用程序多语言

    我正在使用 Visual Studio 2015 在 SilverLight 中创建 Windows Phone 应用程序 8 1 我正在用英语和阿拉伯语创建多语言应用程序 为此 我在项目中创建了 Strings 文件夹 其中包含 en U
  • cuda中有模板化的数学函数吗? [复制]

    这个问题在这里已经有答案了 我一直在寻找 cuda 中的模板化数学函数 但似乎找不到 在普通的 C 中 如果我调用std sqrt它是模板化的 并且将根据参数是浮点数还是双精度数执行不同的版本 我想要这样的 CUDA 设备代码 我的内核将真
  • 如何使用 GCC 在 C 上编译库?

    我用这些文件创建了一个库pila h and pila c 我编译文件pila c with gcc pila c c这个库运行良好 我已经测试过了 然后我又做了一个图书馆 这个库有文件pila funciones extra h and
  • 如何避免函数的多重定义(Linux、GCC/G++、Code::Blocks)

    我有一个代码块项目 它使用许多不同的文件 通常是由其他程序员编写的 目前我遇到的情况是 我有两个不同的子项目 其中包含以相同方式命名的函数 比方说 F int x 因此 F int x 是在两个不同位置的两个源文件中定义的 并且它们有两个不
  • 初学者友好的方法来获取所有文件和目录的列表

    使用 NET 3 0 我得到了下面的方法 它可以正确返回指定目录的所有文件和目录 以及子目录 的集合 如果可能的话 我想将其简化为仅使用我非常熟悉的结构 具体来说 有以下几点我不太清楚 1 IEnumerable
  • 使用全局 Web API 过滤器属性进行 Unity 依赖注入

    参考这个CodePlex 统一文章 http unity codeplex com discussions 446780我能够使用 WebAPI 控制器获取过滤器属性 如下所示 MyFilterAttribute public class
  • C++ 头文件包含

    我正在开发一个项目 每个头文件都有一个预处理器包含防护 我的包含是这样的 文件 gt 包含 main cpp gt header h 字符 h header h gt 矢量 iostream DataFiles h Character h
  • 在.Net中使用ObjectCache缓存对象并设置过期时间

    我陷入了一个场景 我的代码如下 更新 它不是关于如何使用数据缓存 我已经在使用它及其工作 它是关于扩展它 以便该方法在到期时间和从外部源获取新数据之间不会进行调用 object string this GetDataFromCache ca
  • 我如何将 C++ 与 VALA 混合起来

    我需要用 C 编写跨平台的 GUI 应用程序 但由于 C 的大多数 GUI 库都有点乏味 而且我对 C NET 非常熟悉 我发现使用 GTK 的代码 Vala 代码非常有趣 并且与其他方式相比有点容易 那么我该如何将 VAlA 与 C 混合
  • XslCompiledTransform 和自定义 XmlUrlResolver:“具有相同键的条目已存在”

    有没有办法调试由自定义 XmlUrlResolver 从数据库加载的 XSLT 文档 或者有人知道下面的错误消息是关于什么的吗 我有一个导入通用 xslt 文档的 XSLT 样式表

随机推荐