C++模板:Ret(Arg...)的相关

cpp 复制代码
先看一个不保存东西,只解析类型的:

template<typename T>
struct FuncTraits;

// 偏特化:拆解函数类型 R(Args...)
template<typename R, typename... Args>
struct FuncTraits<R(Args...)> {
    using ReturnType = R;
    using ArgsTuple = std::tuple<Args...>;

    static constexpr std::size_t kArity = sizeof...(Args);
};

测试:
    {
        FuncTraits<int(double, char)>::ReturnType i;
        FuncTraits<void()>::ReturnType* j;
        FuncTraits<int(double, char)> o;

        std::ignore = i;
        std::ignore = j;
        std::ignore = o;
    }

如果要保存函数级的变量呢,可能没那么简单。

下面是可以保存lambda的例子(模仿std::function?比如要达成这样的效果:std::function<int(int)> cb1(\[\](int x) { return x; });),一步一步:

cpp 复制代码
template<typename FuncSignature>
struct CallableImplX;

template<typename Ret, typename... Args>
struct CallableImplX<Ret(Args...)>
{

public:
    template<typename _functor>
    explicit CallableImplX(_functor&& f) //: func_(std::move(f))
    {
        std::ignore = 0;
    }
};

引用:
编译通过:
auto lmd = [](int a, int b) ->int {
            return a + b;
        };

CallableImplX<int(int, int)>* p = new CallableImplX<int(int, int)>([](int a, int b) {
            return a + b;
        });
或者自动推导:
auto* p = new CallableImplX<int(int, int)>([](int a, int b) {
            return a + b;
        });

注意,构造函数也是个模板,
由于_functor是在调用构造函数时推导的,
如果没有额外检查的逻辑的话(比如赋值给某个变量),
传入什么参数都可以:
template<typename _functor>
    explicit CallableImplX(_functor f) //: func_(std::move(f))
    {
        std::ignore = 0;
    }

所以,下面的代码也能顺利编译通过:
auto* p2 = new CallableImplX<int(int, int)>(1);
std::ignore = p2;
cpp 复制代码
如果简单的用Ret和Args...组合成函数类型,定义一个成员变量来接收呢?

template<typename FuncSignature>
struct CallableImplX;

template<typename Ret, typename... Args>
struct CallableImplX<Ret(Args...)>
{
    using _funcSig = Ret(Args...); //重新组装成函数类型

public:
    template<typename _functor>
    explicit CallableImplX(_functor&& f) //: func_(std::move(f))
    {
        this->func_ = f; //这个地方,赋值给成员变量。这个地方,编译器会检查。
        std::ignore = 0;
    }

private:
    _funcSig func_; //声明一个成员变量
};

引用:
编译错误:
错误	C2659	"=": 作为左操作数

auto lmd = [](int a, int b) ->int {
            return a + b;
        };

auto* p = new CallableImplX<int(int, int)>([](int a, int b) {
            return a + b;
        });

如何保存呢:

cpp 复制代码
template<typename FuncSignature>
struct CallableImplX;

template<typename Ret, typename... Args>
struct CallableImplX<Ret(Args...)>
{
    using _funcSig = Ret(Args...);

public:
    template<typename _functor>
    explicit CallableImplX(_functor&& f) //: func_(std::move(f))
    {
        this->func_ = f; //这个地方可以正常接收了。
而且std::function也根据Ret(Args...)指定了同样的类型了,所以肯定是没错的
        std::ignore = 0;
    }

private:
    std::function<_funcSig> func_; //用std::function来接收吧
};

引用:
编译通过:
auto lmd = [](int a, int b) ->int {
            return a + b;
        };

auto* p = new CallableImplX<int(int, int)>([](int a, int b) {
            return a + b;
        });

CallableImplX<int(int, int)> o{ [](int a, int b) {
            return a + b;
        } };

但下面会编译出错:
CallableImplX<int(int, int)> o1 = [](int a, int b) {
    return a + b;
   };

错误信息:
CallableImpl<int <lambda>(int, int),int,int,int>::CallableImpl<int <lambda>(int, int),int,int,int>
(
   main::__l3::int <lambda>(int, int) f=int <lambda>(int a, int b){...}
)

FunctorWrapper<int __cdecl(int,int)>::FunctorWrapper<int __cdecl(int,int)>
<
int <lambda>(int, int) >(main::__l3::int <lambda>(int, int) && f=int <lambda>(int a, int b){...}
)

根据AI分析,原因是:
// 错误:explicit构造函数禁止隐式转换
把CallableImplX前面的explicit去掉就能编译通过。

CallableImplX内部存储的是std::function<Ret(Args...)>,而lambda的签名是int(int, int),虽然看起来匹配,但由于构造函数的explicit限制,仍然无法进行隐式转换

擦除型的:

cpp 复制代码
// 抽象基类:定义统一的调用接口
template<typename Ret, typename... Args>
class CallableBase {
public:
    virtual ~CallableBase() = default;
    virtual Ret invoke(Args... args) const = 0;
    virtual CallableBase* clone() const = 0;
};

// 模板子类:存储具体的可调用对象
template<typename F, typename Ret, typename... Args>
class CallableImpl : public CallableBase<Ret, Args...> {
public:
    explicit CallableImpl(F f) : func_(std::move(f)) {
        std::ignore = 0;
    }

    Ret invoke(Args... args) const override {
        return func_(std::forward<Args>(args)...);
    }

    CallableBase<Ret, Args...>* clone() const override {
        return new CallableImpl(func_);
    }

private:
    F func_; // 存储lambda的副本
};


// 主模板:接收可调用对象,使用类型擦除
template<typename Signature>
class FunctorWrapper;

// 偏特化:处理具体的函数签名
template<typename Ret, typename... Args>
class FunctorWrapper<Ret(Args...)> {
public:
    // 模板构造函数:接收任意可调用对象
    template<typename F>
    FunctorWrapper(F&& f)
        : impl_(new CallableImpl<std::decay_t<F>, Ret, Args...>(std::forward<F>(f))) {}

    // 拷贝构造
    FunctorWrapper(const FunctorWrapper& other)
        : impl_(other.impl_ ? other.impl_->clone() : nullptr) {}

    // 移动构造
    FunctorWrapper(FunctorWrapper&& other) noexcept
        : impl_(other.impl_) {
        other.impl_ = nullptr;
    }

    // 析构
    ~FunctorWrapper() {
        delete impl_;
    }

    // 调用操作符
    Ret operator()(Args... args) const {
        if (!impl_) {
            throw std::runtime_error("Calling empty FunctorWrapper!");
        }
        return impl_->invoke(std::forward<Args>(args)...);
    }

    // 判断是否为空
    explicit operator bool() const {
        return impl_ != nullptr;
    }

private:
    CallableBase<Ret, Args...>* impl_ = nullptr;
};

测试:
// 1. 接收无捕获的lambda
FunctorWrapper<int(int, int)> adder = [](int a, int b) {
      return a + b;
};
std::cout << "adder(3, 4) = " << adder(3, 4) << std::endl; // 输出7
相关推荐
青少儿编程课堂25 分钟前
蓝桥杯青少组图形化 + 全国青少年信息素养大赛 + 电子学会Scratch考级:校园连连看解析
c++·python·算法·bfs·信息学竞赛
kongdong37 分钟前
Hikyuu | RSI均值回归交易策略实现
算法·均值算法·回归·量化交易·交易策略·hikyuu
小王C语言1 小时前
优选算法:分治-归并排序
算法
草莓熊Lotso1 小时前
【C++ 基于微服务的即时通讯系统】文件存储子服务实战:UUID生成、文件IO封装与全接口单元测试
开发语言·网络·数据库·c++·微服务·架构·单元测试
huaqianzkh2 小时前
WinForm C#:循环修改控件UI闪烁卡顿?SuspendLayout / ResumeLayout 与 BeginUpdate 优化
开发语言·ui·c#
非优秀程序员11 小时前
第05章 播放器大战,暴风、快播与那一夜|《互联网产品恩仇录》
算法
沐晓时光12 小时前
C语言入门,内存函数知识讲解
c语言·开发语言
武子康18 小时前
驾驶轨迹换个坐标,就能给移动机器人用吗?
人工智能·算法·机器学习
David猪大卫18 小时前
【C++修炼】map 和 set 的使用
c语言·开发语言·数据结构·c++·经验分享·笔记·学习