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
相关推荐
小白学大数据1 小时前
新闻爬虫开发实战:Python 搞定新闻网站关键词文章抓取
开发语言·爬虫·python·自动化
Chase_______1 小时前
LeetCode 3 & 3090 题解:不定长滑动窗口——从“不重复“到“最多两次“,一个模板搞定频次约束问题
算法·leetcode
Overboom1 小时前
[BEV感知] --- IPM算法
数码相机·算法
weiabc1 小时前
整数最接近等因数分解函数(汇编优化版)
开发语言·前端·javascript
Highcharts.js1 小时前
专为软件团队打造的数据可视化开发工具|Highcharts图表
开发语言·信息可视化·highcharts·实战代码
yuanpan1 小时前
Python + sqlite3 本地 SQLite 数据库操作实战:完整 CRUD 入门教程
开发语言·python·opencv
rit84324991 小时前
水声通信Rake接收机-MATLAB
开发语言·matlab
sindyra1 小时前
享元模式(Flyweight Pattern)
java·开发语言·设计模式·享元模式·优缺点
codingPower1 小时前
ApplicationListener 和 SpringApplicationRunListener 深度解析对比
java·开发语言·spring boot