如何在C++模板类构造函数中分发多参数包初始化多个仿函数?
问题:直接通过Composer构造函数完成三个仿函数的初始化
现有一段无法正常运行的C代码,目标是实现一个Composer模板类,串联三个仿函数(StageOne、StageTwo、StageThree),并希望直接通过Composer的构造函数完成这三个仿函数的初始化,而非额外调用setStageOne这类方法。原代码的问题在于构造函数的模板参数包写法不符合C推导规则,导致无法编译。
原代码如下:
#include<iostream> template<typename T1, typename T2, typename T3> class Composer { // pseudocode of constructor template<typename... Args_one, typename... Args_two, typename... Args_three> explicit Composer(Args_one...args1, Args_two...args2, Args_three...args3) { t1_ = T1(args1...); t2_ = T2(args2...); t3_ = T3(args3...); } double operator()(int u) { auto x1 = t1_(u); auto x2 = t2_(float(x1)); auto x3 = t3_(double(x2)); return x3; } private: T1 t1_; T2 t2_; T3 t3_; }; class StageOne { public: StageOne(int a, int b): a_(a), b_(b) {}; int operator()(int c) const { return a_ + b_ + c; } private: int a_; int b_; }; class StageTwo { public: StageTwo(float c, float d): c_(c), d_(d) {}; float operator()(float i) const { return c_ + d_ + i; } private: float c_; float d_; }; class StageThree { public: StageThree(double x, double y): x_(x), y_(y) {}; double operator()(double z) const { return (x_ + y_) * z; } private: double x_; double y_; }; int main() { Composer<StageOne, StageTwo, StageThree> composer{{3, 4}, {1.2f, 2.3f}, {4.5, 3.3}}; std::cout << composer(3) << std::endl; }
可行解决方案
方案一:直接接受已构造的仿函数对象
最简单的方式是让Composer的构造函数直接接收三个已经初始化好的T1、T2、T3对象,语法合规且直观:
#include<iostream> #include<utility> template<typename T1, typename T2, typename T3> class Composer { public: explicit Composer(T1 t1, T2 t2, T3 t3) : t1_(std::move(t1)), t2_(std::move(t2)), t3_(std::move(t3)) {} double operator()(int u) { auto x1 = t1_(u); auto x2 = t2_(static_cast<float>(x1)); auto x3 = t3_(static_cast<double>(x2)); return x3; } private: T1 t1_; T2 t2_; T3 t3_; }; // StageOne、StageTwo、StageThree类保持不变 int main() { Composer<StageOne, StageTwo, StageThree> composer( StageOne(3, 4), StageTwo(1.2f, 2.3f), StageThree(4.5, 3.3) ); std::cout << composer(3) << std::endl; }
方案二:用tuple包裹构造参数并展开
如果希望直接传入构造参数而非提前构造Stage对象,可以用std::tuple包裹每个Stage的参数,再通过std::apply展开初始化成员:
#include<iostream> #include<tuple> #include<utility> template<typename T1, typename T2, typename T3> class Composer { public: template<typename... Args1, typename... Args2, typename... Args3> explicit Composer(std::tuple<Args1...> args1, std::tuple<Args2...> args2, std::tuple<Args3...> args3) : t1_(std::apply([](auto&&... args){ return T1(std::forward<decltype(args)>(args)...); }, std::move(args1))), t2_(std::apply([](auto&&... args){ return T2(std::forward<decltype(args)>(args)...); }, std::move(args2))), t3_(std::apply([](auto&&... args){ return T3(std::forward<decltype(args)>(args)...); }, std::move(args3))) {} double operator()(int u) { auto x1 = t1_(u); auto x2 = t2_(static_cast<float>(x1)); auto x3 = t3_(static_cast<double>(x2)); return x3; } private: T1 t1_; T2 t2_; T3 t3_; }; // StageOne、StageTwo、StageThree类保持不变 int main() { Composer<StageOne, StageTwo, StageThree> composer( std::make_tuple(3, 4), std::make_tuple(1.2f, 2.3f), std::make_tuple(4.5, 3.3) ); std::cout << composer(3) << std::endl; }
方案三:利用聚合初始化(C++17及以上)
将Composer改为聚合类(无用户声明构造函数、非静态成员均为public),可直接用聚合初始化语法完成成员初始化:
#include<iostream> template<typename T1, typename T2, typename T3> class Composer { public: double operator()(int u) { auto x1 = t1_(u); auto x2 = t2_(static_cast<float>(x1)); auto x3 = t3_(static_cast<double>(x2)); return x3; } T1 t1_; T2 t2_; T3 t3_; }; // StageOne、StageTwo、StageThree类保持不变 int main() { Composer<StageOne, StageTwo, StageThree> composer{ StageOne(3, 4), StageTwo(1.2f, 2.3f), StageThree(4.5, 3.3) }; std::cout << composer(3) << std::endl; }
原代码编译失败原因
原构造函数的写法违反C++模板推导规则:模板参数包只能放在函数参数列表的最后一位,编译器无法区分args1、args2、args3的参数边界,因此无法推导三个参数包的类型和数量,导致编译失败。
内容的提问来源于stack exchange,提问作者H. Yong
相关产品推荐
相关产品推荐

