如何移除C++中手动索引的大括号列表样板代码?
解决方案
要同时保留手动定义单个数组(如A0、A1)的便捷性,又消除A[]、x[]、b[]的样板代码,可以借助预处理器宏来自动生成指针数组的初始化列表。由于每个派生类(如BaseCat、BaseDog)的n值在编写代码时是固定的,这种方法完全可行。
步骤1:定义生成指针列表的宏
首先,为常用的n值(如2、3、4、24等)定义宏,用于生成对应数量的指针列表:
// 基础宏:生成单个指针项 #define GEN_PTR_ITEM(name, idx) name##idx // 为不同n值定义指针列表宏 #define GEN_PTR_LIST_2(name) GEN_PTR_ITEM(name, 0), GEN_PTR_ITEM(name, 1) #define GEN_PTR_LIST_3(name) GEN_PTR_LIST_2(name), GEN_PTR_ITEM(name, 2) #define GEN_PTR_LIST_4(name) GEN_PTR_LIST_3(name), GEN_PTR_ITEM(name, 3) // ... 按需扩展到n=24的版本: #define GEN_PTR_LIST_24(name) GEN_PTR_LIST_23(name), GEN_PTR_ITEM(name, 23) // 最终宏:生成完整的指针数组声明 #define GEN_PTR_ARRAY(name, n) double* name[] = { GEN_PTR_LIST_##n(name) }
步骤2:在派生类中使用宏
保留原有的单个数组定义,并用宏替代样板代码:
template<size_t n> class Base{ public: static constexpr size_t n_ = n; }; class BaseCat: protected Base<4>{ private: // 保留原有的便捷初始化方式 double A0[] = {0,1,2,3,4,5,6,7,8,9}; double A1[] = {13,-2}; double A2[] = {4.5, 6.7, 8.9}; double A3[] = {-1, -2, -3, -4}; double x0[] = {1,0}; double x1[] = {0,1}; double x2[] = {1,1}; double x3[] = {0,0}; double b0[] = {0}; double b1[] = {1}; double b2[] = {2}; double b3[] = {3}; protected: // 用宏替代样板代码 GEN_PTR_ARRAY(A, 4) GEN_PTR_ARRAY(x, 4) GEN_PTR_ARRAY(b, 4) }; using DerivedCat = Derived<BaseCat, BaseCat::n_>;
替代方案:CRTP + 元组(C++17及以上)
如果你愿意调整单个数组的声明方式,可以使用CRTP(奇异递归模板模式)结合std::tuple来完全消除样板代码,无需宏:
#include <tuple> #include <array> #include <utility> template<size_t n, typename Derived> class Base{ public: static constexpr size_t n_ = n; protected: double* A[n]; double* x[n]; double* b[n]; Base(){ auto& derived = *static_cast<Derived*>(this); // 利用索引序列自动初始化指针数组 [&]<size_t... Is>(std::index_sequence<Is...>) { ((A[Is] = std::get<Is>(derived.A_tuple).data()), ...); ((x[Is] = std::get<Is>(derived.x_tuple).data()), ...); ((b[Is] = std::get<Is>(derived.b_tuple).data()), ...); }(std::make_index_sequence<n>{}); } }; class BaseCat: protected Base<4, BaseCat>{ private: // 将单个数组放入元组中,保持初始化便捷性 std::tuple< std::array<double, 10>, std::array<double, 2>, std::array<double, 3>, std::array<double, 4> > A_tuple = { {0,1,2,3,4,5,6,7,8,9}, {13,-2}, {4.5,6.7,8.9}, {-1,-2,-3,-4} }; std::tuple< std::array<double,2>, std::array<double,2>, std::array<double,2>, std::array<double,2> > x_tuple = { {1,0}, {0,1}, {1,1}, {0,0} }; std::tuple< std::array<double,1>, std::array<double,1>, std::array<double,1>, std::array<double,1> > b_tuple = { {0}, {1}, {2}, {3} }; // 无需手动声明A[]、x[]、b[],由Base构造器自动初始化 }; using DerivedCat = Derived<BaseCat, BaseCat::n_>;
这种方案虽然需要将数组放入元组,但完全避免了样板代码,且类型更安全。
内容的提问来源于stack exchange,提问作者HKoplin
相关产品推荐
相关产品推荐

