【项目三 (利用remove_reference将引用类型转化为值类型,从而实现对任何非void类型函数的返回值存储)】C++实现事件委托(完全体)

文章介绍了作者在实现C++事件委托时遇到的问题,包括无返回值无参数函数的模板匹配和返回引用类型的处理。通过研究C++11的std::function,作者找到了解决方案,利用模板特化和std::remove_reference来处理不同类型的返回值。文章还展示了如何通过显式具体化模板解决void返回值问题,并提供了使用示例。此外,作者引入了一个无返回值调用接口以提高效率。
摘要由CSDN通过智能技术生成

前言

        之前的版本:

        【项目三】C++实现事件委托_学艺不精的Антон的博客-CSDN博客

        前两天因为学设计模式的观察者模式,参考了网上的资料尝试用C++来实现事件委托

        当时完成的版本主要有以下两个问题:

                1. 对于无返回值无参数的函数委托,在声明事件委托的时候Delegate<void,void>,是模板参数匹配的问题,只能写成Delegate<void>

                2. 按照参考的博主写的事件委托在添加引用类型返回值时,由于vector无法存储引用类型会报错,所以我的初始实现索性就省略掉了返回函数返回值的线性表的操作

        本文我将一步一步介绍如何解决以上两个问题,本文只给出部分代码,需要完整代码可以克隆我的仓库(建议完整克隆,项目依赖我编写的异常类库):

        AntonaStandard: 自定义的库,里面编写了一些常用的工具 (gitee.com)

解决思路

        今天在翻C++PrimerPlus的时候发现了C++“新标准”(C++11)的一个机制,包装器function,包装器function也是对函数(类的静态与非静态成员函数,以及普通函数)进行多态封装,于是就查找了相关的资料:

        STL源码分析之std::function - 知乎 (zhihu.com)

里面有function的部分声明:

template<typename _Res, typename... _ArgTypes>
class function<_Res(_ArgTypes...)>
  : public _Maybe_unary_or_binary_function<_Res, _ArgTypes...>
  , private _Function_base
{
private:
    using _Invoker_type = _Res (*)(const _Any_data&, _ArgTypes&&...);
    _Invoker_type _M_invoker;
// ... ...
// 还有很多成员函数
};

这个声明就很奇妙,因为它允许我像下面这样声明包装器

function<void(void)> func;
function<void()> func_1;

想必我遗漏了什么知识点,于是查找了一下模板相关的资料,这么写应该是类似显式具体化的写法(本人水平有限,找了一下午也没找到明确说明这叫什么的资料),将一堆子模板参数具体化成一个函数类型。因此第一个问题就得到了解决,只需要将事件委托的特化版本声明出来就可以:

template<typename type_RETURN_VALUE, typename... type_PARAMETERS_PACK>
class Delegate<type_RETURN_VALUE(type_PARAMETERS_PACK...)>{
.........
}

remove_reference也是偶然翻到的,先简单说明一下使用方式和实现原理

使用:

// 使用方式
remove_reference<int&>::type a = 10;		// 此时的type表示int类型
	// 当然也可以脱去右值引用
remove_reference<int&&>::type b = a;		// type为int类型

在完成初版的时候,如何解决将类型引用转化为该类型的值类型困扰了我好久。有时不得不感叹真理往往是简介明了的,下面我截取了remove_reference的源码:

// Reference transformations.

/// remove_reference
template<typename _Tp>
struct remove_reference
{ typedef _Tp   type; };

template<typename _Tp>
struct remove_reference<_Tp&>
{ typedef _Tp   type; };

template<typename _Tp>
struct remove_reference<_Tp&&>
{ typedef _Tp   type; };


可以看到,通过模板参数的表达式,编译器自动选择合适的形式具体化版本,然后在模板的不同实现中,可以根据对应的参数形式推断出值类型。

因此在有返回值的多播委托中将重载运算发()声明成如下的形式就可以返回保存委托的线性表了

virtual  std::vector<typename std::remove_reference<type_RETURN_VALUE>::type> operator()(type_PARAMETERS_PACK... parama_args);

注意,要在std::remove_reference<type_RETURN_VALUE>::type添加关键字typename告诉编译器type是一个类型而不是一个静态成员。

这样所有问题就都解决了,只需要把初版的声明修改一下,显式具体化出void返回值版的事件委托类

// 委托类
    template<typename type_RETURN_VALUE, typename... type_PARAMETERS_PACK>
    class Delegate<type_RETURN_VALUE(type_PARAMETERS_PACK...)>{
        // 通过链表存储容器
    public:
        // 声明一下链表和迭代器,化简表达
        using DelegateList = std::list<BaseFuncPointerContainer<type_RETURN_VALUE,type_PARAMETERS_PACK...>* >;
        using DelegateListIterator = typename DelegateList::iterator;
        using DelegateListConstIterator = typename DelegateList::const_iterator;
    protected:
        DelegateList delegateList;

    public:
        // 声明为虚函数,提高可拓展性
            // 获取委托个数
        inline virtual unsigned long size()const{
            return this->delegateList.size();
        }
            // 判断委托是否为空
        inline virtual bool empty()const{
            return this->delegateList.empty();
        }
            // 清空委托
        virtual void clear();
        virtual Delegate<type_RETURN_VALUE(type_PARAMETERS_PACK...)>& operator+=(BaseFuncPointerContainer<type_RETURN_VALUE,type_PARAMETERS_PACK...>* other_ptr);
        virtual Delegate<type_RETURN_VALUE(type_PARAMETERS_PACK...)>& operator-=(BaseFuncPointerContainer<type_RETURN_VALUE,type_PARAMETERS_PACK...>* other_ptr);
            // 多播返回值存储到一个vector中
        virtual  std::vector<typename std::remove_reference<type_RETURN_VALUE>::type> operator()(type_PARAMETERS_PACK... parama_args);
            // 返回值如果要存到一个vector中效率较低,这里提供一个无返回值的接口
        virtual void call_without_return(type_PARAMETERS_PACK... parama_args);

        //  左值赋值构造函数,和赋值函数
        Delegate(const Delegate&);
        Delegate<type_RETURN_VALUE(type_PARAMETERS_PACK...)>& operator=(const Delegate<type_RETURN_VALUE(type_PARAMETERS_PACK...)>&);
        
        // 右值移动构造函数,和赋值函数
        Delegate(Delegate&&);
        Delegate<type_RETURN_VALUE(type_PARAMETERS_PACK...)>& operator=(const Delegate<type_RETURN_VALUE(type_PARAMETERS_PACK...)>&&);
        
        // 空构造函数
        Delegate(){};
        // 虚析构函数
        virtual ~Delegate(){this->clear();};
    };

    // 委托类,返回值为void的具体化版本
    template<typename... type_PARAMETERS_PACK>
    class Delegate<void(type_PARAMETERS_PACK...)>{
        // 通过链表存储容器
    public:
        // 声明一下链表和迭代器,化简表达
        using DelegateList = std::list<BaseFuncPointerContainer<void,type_PARAMETERS_PACK...>* >;
        using DelegateListIterator = typename DelegateList::iterator;
        using DelegateListConstIterator = typename DelegateList::const_iterator;
    protected:
        DelegateList delegateList;

    public:
        // 声明为虚函数,提高可拓展性
            // 获取委托个数
        inline virtual unsigned long size()const{
            return this->delegateList.size();
        }
            // 判断委托是否为空
        inline virtual bool empty()const{
            return this->delegateList.empty();
        }
            // 清空委托
        virtual void clear();
        virtual Delegate<void(type_PARAMETERS_PACK...)>& operator+=(BaseFuncPointerContainer<void,type_PARAMETERS_PACK...>* other_ptr);
        virtual Delegate<void(type_PARAMETERS_PACK...)>& operator-=(BaseFuncPointerContainer<void,type_PARAMETERS_PACK...>* other_ptr);
            // 暂时禁用多播返回值,因为如果type_RETURN_VALVE为一个引用类型的话std::vector无法进行存储
        virtual void operator()(type_PARAMETERS_PACK... parama_args);
        // virtual void operator()(type_PARAMETERS_PACK... parama_args);

        //  左值赋值构造函数,和赋值函数
        Delegate(const Delegate&);
        Delegate<void(type_PARAMETERS_PACK...)>& operator=(const Delegate<void(type_PARAMETERS_PACK...)>&);
        
        // 右值移动构造函数,和赋值函数
        Delegate(Delegate&&);
        Delegate<void(type_PARAMETERS_PACK...)>& operator=(const Delegate<void(type_PARAMETERS_PACK...)>&&);
        
        // 空构造函数
        Delegate(){};
        // 虚析构函数
        virtual ~Delegate(){this->clear();};
    };

另外,由于添加返回值在创建线性表vector的时候必然会消耗运行效率,我为此在通用特化版本中添加了另一个调用接口

virtual void call_without_return(type_PARAMETERS_PACK... parama_args);

该接口由于显式具体化的特性,只有声明了该接口的版本才拥有该接口,因此返回值是void的Delegate不具有该接口。客户端可以根据需要有返回值地调用委托或者无返回值地调用委托

最后给出一个演示示例

#include "Delegate.h"
#include <iostream>
#include <vector>
using namespace std;
using namespace AntonaStandard;
class A{
public:
    static int s_i_v_A(){
        cout<<"int返回值无参数A的静态函数"<<endl;
        return 10;
    }
    int i_v_A(){
        cout<<"int返回值无参数A的非静态函数"<<endl;
        return 11;
    }

};
int i_v(){
    cout<<"int返回值无参数普通函数"<<endl;
    return 12;
}
class B{
public:
    static void s_v_v_B(){
        cout<<"无返回值无参数B的静态函数"<<endl;
    }
    void v_v_B(){
        cout<<"无返回值无参数B的非静态函数"<<endl;
    }
};
void v_v(){
    cout<<"无返回值无参数的普通函数"<<endl;
}

class C{
public:
    static int& s_iq_iq_C(int& v){
        cout<<"无返回值无参数C的静态函数 "<<v<<endl;
        return v;
    }
    int& iq_iq_C(int& v){
        cout<<"无返回值无参数C的非静态函数 "<<v<<endl;
        return v;
    }
};
int& iq_iq(int& v){
    cout<<"无返回值无参数的普通函数 "<<v<<endl;
    return v;
}

int main(){
    A a;
    Delegate<int(void)> del;
    del += newDelegate(A::s_i_v_A);
    del += newDelegate(a,a.i_v_A);
    del += newDelegate(i_v);
    vector<int> vec = del();
    cout<<"--------------"<<endl;
    del.call_without_return();
    for(int i = 0;i<vec.size();++i){
        cout<<vec[i]<<" ";
    }
    cout<<endl<<"--------------"<<endl;
    B b;
    Delegate<void()> del1;
    del1 += newDelegate(B::s_v_v_B);
    del1 += newDelegate(b,b.v_v_B);
    del1 += newDelegate(v_v);
    del1();
    cout<<endl<<"--------------"<<endl;
    C c;
    Delegate<int&(int&)> del2;
    del2 += newDelegate(C::s_iq_iq_C);
    del2 += newDelegate(c,c.iq_iq_C);
    del2 += newDelegate(iq_iq);
    int x = 666;
    vector<int> vec2 = del2(x);
    for(int i = 0;i<vec2.size();++i){
        cout<<vec2[i]<<" ";
    }
    return 0;
}

        输出如下:

int返回值无参数A的静态函数
int返回值无参数A的非静态函数
int返回值无参数普通函数
--------------
int返回值无参数A的静态函数
int返回值无参数A的非静态函数
int返回值无参数普通函数
10 11 12
--------------
无返回值无参数B的静态函数
无返回值无参数B的非静态函数
无返回值无参数的普通函数

--------------
无返回值无参数C的静态函数 666
无返回值无参数C的非静态函数 666
无返回值无参数的普通函数 666
666 666 666 

总结

        整个项目大概搞了两天多,期间改了不少的版本:

 现在的事件委托可以是说是比较完善的了,这个项目可以说是一个跨年项目,希望大家多多支持,如果给您了什么思路,求一个赞吧和😜,线上的仓库我会不断更新,用C++实现更多的机制,项目开源,最后祝各位新年快乐!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学艺不精的Антон

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值