You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++简化函数模板消重时的decltype相关编译问题求解

成员函数包装类模板精简方案

问题背景

初始实现的类成员函数包装类存在实例化语法冗余的问题,需要重复书写成员函数指针的类型与对应值,尝试编写别名模板精简时触发编译错误:

'Retriever': is not a valid type for non-type template parameter 'retrieverFunc'

初始冗余实现代码

#include <cstdio>
#include <type_traits>
#include <utility>

using namespace std;

class testclass {
public:
    double get() { return d_; }
    void set(double d) { d_ = d; }
    double d_ = 0.0;
};

template<typename Retriever, Retriever retrieverFunc, typename Updater, Updater updaterFunc, typename OwningClass>
struct Wrapper {
    Wrapper(Retriever retriever, Updater updater, OwningClass* owner) : retriever_(retriever), updater_(updater), containingClass_(owner) {}

    using GetterReturnType = std::invoke_result_t<decltype(retrieverFunc), OwningClass>;
    GetterReturnType Get() { return (containingClass_->*retriever_)(); }

    template<typename...Args>
    using SetterReturnType = std::invoke_result_t<decltype(updaterFunc), OwningClass, Args...>;

    template<typename...Args>
    SetterReturnType<Args...> Set(Args&&... args) { return (containingClass_->*updater_)((forward<Args>(args))...); }

    Retriever retriever_;
    Updater updater_;
    OwningClass* containingClass_;
};

int main() {
    testclass tc;
    // 模板参数重复书写类型和函数指针,非常繁琐
    Wrapper<decltype(&testclass::get), &testclass::get, decltype(&testclass::set), &testclass::set, testclass> pp(&testclass::get, &testclass::set, &tc);
    pp.Get();
    pp.Set(1.0);
}

错误的别名模板写法

// 存在语法和逻辑错误
template<typename Retriever, Wrapper Updater, typename OwningClass>
using WrapperSimplified = Wrapper<decltype(Retriever), Retriever, decltype(Updater), Updater, OwningClass>;

错误原因

  • 用typename声明的是类型模板形参,但传入的&testclass::get是成员函数指针(非类型值),形参类型和实参不匹配
  • 非类型模板参数在C++17前必须显式指定类型,无法直接从传入的值推导
  • 别名模板中Wrapper Updater是语法错误,无意义地给Updater参数加了Wrapper前缀

修复方案

根据使用的C++标准版本,二选一即可:

方案1:C++17及以上版本,用auto非类型模板参数

C++17支持用auto声明非类型模板参数,自动推导参数类型,无需重复书写类型:

  1. 编写正确的别名模板
template<auto retrieverFunc, auto updaterFunc, typename OwningClass>
using WrapperSimplified = Wrapper<
    decltype(retrieverFunc), retrieverFunc,
    decltype(updaterFunc), updaterFunc,
    OwningClass
>;
  1. 可选优化:因为模板参数已经固定了两个成员函数指针,不需要在构造函数重复传入,可以修改Wrapper的构造函数进一步精简代码
template<typename Retriever, Retriever retrieverFunc, typename Updater, Updater updaterFunc, typename OwningClass>
struct Wrapper {
    // 构造函数仅需传入宿主对象指针
    Wrapper(OwningClass* owner) : containingClass_(owner) {}

    using GetterReturnType = std::invoke_result_t<decltype(retrieverFunc), OwningClass>;
    GetterReturnType Get() { return (containingClass_->*retrieverFunc)(); }

    template<typename...Args>
    using SetterReturnType = std::invoke_result_t<decltype(updaterFunc), OwningClass, Args...>;

    template<typename...Args>
    SetterReturnType<Args...> Set(Args&&... args) { 
        return (containingClass_->*updaterFunc)(std::forward<Args>(args)...); 
    }

    OwningClass* containingClass_;
};
  1. 实例化代码非常简洁,无冗余
int main() {
    testclass tc;
    WrapperSimplified<&testclass::get, &testclass::set, testclass> pp(&tc);
    pp.Get();
    pp.Set(1.0);
    return 0;
}

方案2:C11/C14版本,用工厂函数自动推导

低版本C++没有auto非类型模板参数,可以通过工厂函数的实参推导自动获取类型,避免手动写冗长的模板参数:

template<typename Retriever, typename Updater, typename OwningClass>
auto MakeWrapper(Retriever r, Updater u, OwningClass* owner) {
    return Wrapper<Retriever, r, Updater, u, OwningClass>(r, u, owner);
}

使用时无需手动填写模板参数:

int main() {
    testclass tc;
    auto pp = MakeWrapper(&testclass::get, &testclass::set, &tc);
    pp.Get();
    pp.Set(1.0);
    return 0;
}

内容的提问来源于stack exchange,提问作者Wad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 02:27:20