C++:派生类constexpr能否不实例化类初始化基类成员并实现静态getData
问题
希望将通用功能仅在基类中实现一次,存在大量结构相同但数据不同的应用场景,每个派生类对应自己的编译期确定的数据集(由外部代码自动生成)。
当前代码需要实例化派生类才能调用getData方法,现希望将基类的getData改为静态方法,无需实例化即可通过myDerived1::getData(1)调用,同时去掉仅在实例化时才会调用的构造函数,解决基类成员初始化的问题。
原可编译代码如下:
#include<array> #include <iostream> struct myStruct { int id; int size; }; class myBase { protected: const myStruct *myStructArray; myBase(const myStruct *structArray) { myStructArray = structArray; }; public: // 实际场景中该功能复杂,希望仅在基类定义一次,避免在派生类重复实现导致维护困难 // 所有数据编译期即可确定,希望能静态调用:myDerived1::getData(1); 无需实例化类 myStruct getData(int index) { return *(myStructArray + index); } }; // 示例派生类1,定义自有数据集 class myDerived1 : public myBase { constexpr static auto carrFieldMetaData{ []() { // 用lambda在constexpr中显式初始化数组索引 std::array<myStruct, 2> myStructData{}; myStructData[0] = {.id = 0, .size = 1}; myStructData[1] = {.id = 1, .size = 2}; return myStructData; }()}; public: myDerived1() : myBase(carrFieldMetaData.data()){}; }; // 示例派生类2,结构相同但数据不同 class myDerived2 : public myBase { constexpr static auto carrFieldMetaData{ []() { // 用lambda在constexpr中显式初始化数组索引 std::array<myStruct, 3> myStructData{}; myStructData[0] = {.id = 0, .size = 8}; myStructData[1] = {.id = 1, .size = 9}; myStructData[2] = {.id = 2, .size = 1}; return myStructData; }()}; public: myDerived2() : myBase(carrFieldMetaData.data()){}; }; int main(int argc, char** argv) { myBase test = myDerived1(); std::cout << test.getData(0).size << '\n'; std::cout << test.getData(1).size << '\n'; };
可行方案
可以利用**奇异递归模板模式(CRTP)**实现需求,让基类作为模板类,通过模板参数关联派生类,直接访问派生类的编译期静态数据集,从而实现静态调用getData且无需实例化类。修改后的代码如下:
#include<array> #include <iostream> struct myStruct { int id; int size; }; // CRTP基类,模板参数为派生类 template<typename Derived> class myBase { public: // 静态方法,直接访问派生类的静态数据集 static myStruct getData(int index) { // 调用派生类的静态方法获取数据集 return Derived::getDataSet()[index]; } }; // 派生类1:继承CRTP基类,定义自有编译期数据集 class myDerived1 : public myBase<myDerived1> { // 编译期初始化的静态数据集 constexpr static std::array<myStruct, 2> carrFieldMetaData{ []() { std::array<myStruct, 2> data{}; data[0] = {.id = 0, .size = 1}; data[1] = {.id = 1, .size = 2}; return data; }() }; public: // 提供静态方法供基类访问数据集 static constexpr const auto& getDataSet() { return carrFieldMetaData; } }; // 派生类2:结构相同,仅数据不同 class myDerived2 : public myBase<myDerived2> { constexpr static std::array<myStruct, 3> carrFieldMetaData{ []() { std::array<myStruct, 3> data{}; data[0] = {.id = 0, .size = 8}; data[1] = {.id = 1, .size = 9}; data[2] = {.id = 2, .size = 1}; return data; }() }; public: static constexpr const auto& getDataSet() { return carrFieldMetaData; } }; int main(int argc, char** argv) { // 无需实例化,直接静态调用 std::cout << myDerived1::getData(0).size << '\n'; std::cout << myDerived1::getData(1).size << '\n'; std::cout << myDerived2::getData(0).size << '\n'; std::cout << myDerived2::getData(1).size << '\n'; std::cout << myDerived2::getData(2).size << '\n'; };
关键改动说明
- CRTP模板基类:将
myBase改为模板类,模板参数为派生类,让基类能够直接访问派生类的静态成员。 - 移除非静态成员与构造函数:不再需要
myStructArray成员和构造函数,所有数据访问直接通过派生类的静态数据集完成。 - 静态方法访问数据集:派生类提供
getDataSet()静态方法,返回编译期初始化的constexpr数组,基类的getData静态方法直接调用该方法获取数据。 - 编译期安全性:所有数据集均为
constexpr,确保数据在编译期完全确定,运行时无额外开销。
内容的提问来源于stack exchange,提问作者Stefan
相关产品推荐
相关产品推荐

