对象构造后向成员方法传递可变参数的优化方案咨询
可变参数模板类的性能优化方案
问题描述
需要构建一个模板类,使其在构造时接收可变参数后,成员函数可直接使用这些参数无需用户重复传入。现有实现通过std::tuple存储构造参数,再借助callExpr函数和std::index_sequence将参数展开传递给expression函数,但认为这种中间调用带来了极高的性能开销,希望找到更高效的实现方式。
现有代码如下:
struct FunctionBase { virtual ~FunctionBase() {} }; template<typename T, typename... Args> struct Function : public FunctionBase { public: Function(Args... params) : argsTuple(params...) {} const virtual T operator()(T x) { return callExpr(x, std::index_sequence_for<Args...>{}); } protected: template<std::size_t... I> const T callExpr(T x, std::index_sequence<I...>) { return expression(x, std::get<I>(argsTuple)...); } const virtual T expression(T x, Args... params) { // 执行算术操作 return 0; } std::tuple<Args...> argsTuple; };
优化方案
1. 直接存储参数为成员变量(替代tuple)
如果不需要将参数打包为单个对象,可以直接把可变参数声明为类的成员变量,跳过tuple和std::get的开销。C++17及以上支持折叠初始化语法,实现起来非常简洁:
struct FunctionBase { virtual ~FunctionBase() {} }; template<typename T, typename... Args> struct Function : public FunctionBase { public: // 完美转发构造参数,初始化成员变量 Function(Args&&... params) : m_args(std::forward<Args>(params))... {} const T operator()(T x) override { // 直接传递成员变量给expression return expression(x, m_args...); } protected: virtual const T expression(T x, Args... params) override { // 执行算术操作 return 0; } // 直接存储每个参数作为成员 std::decay_t<Args> m_args...; };
注:std::decay_t用于处理参数的引用、cv限定符等,确保成员变量类型符合常规存储需求;如果需要保留引用类型,可以去掉std::decay_t。
2. 用std::apply简化参数展开(保留tuple)
如果必须使用tuple存储参数(比如需要将参数作为整体传递),可以用C++17引入的std::apply替代自定义的callExpr函数,代码更简洁且编译器对标准库函数的优化更充分:
#include <tuple> struct FunctionBase { virtual ~FunctionBase() {} }; template<typename T, typename... Args> struct Function : public FunctionBase { public: Function(Args... params) : argsTuple(params...) {} const T operator()(T x) override { // 用std::apply直接展开tuple参数 return std::apply( [this, x](auto&&... params) { return this->expression(x, std::forward<decltype(params)>(params)...); }, argsTuple ); } protected: virtual const T expression(T x, Args... params) override { // 执行算术操作 return 0; } std::tuple<Args...> argsTuple; };
3. 移除多态开销(如果不需要基类)
如果FunctionBase的多态需求不是必须的,建议直接去掉基类和virtual关键字。编译器可以对非虚函数进行完全内联优化,彻底消除所有中间调用的开销:
template<typename T, typename... Args> struct Function { public: Function(Args&&... params) : m_args(std::forward<Args>(params))... {} const T operator()(T x) { return expression(x, m_args...); } protected: // 非虚函数,便于编译器内联优化 const T expression(T x, Args... params) { // 执行算术操作 return 0; } std::decay_t<Args> m_args...; };
关键性能提示
原实现中callExpr和std::get的开销在开启编译器优化(如-O2或-O3)后几乎可以忽略——编译器会自动内联这些函数并消除冗余操作。如果实际运行中性能仍低,大概率是virtual函数调用的开销(虚函数表查找无法被内联),此时可以考虑移除多态设计,或用CRTP(奇异递归模板模式)替代虚函数实现静态多态。
内容的提问来源于stack exchange,提问作者Marco Tullio
相关产品推荐
相关产品推荐

