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

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';
};

关键改动说明

  1. CRTP模板基类:将myBase改为模板类,模板参数为派生类,让基类能够直接访问派生类的静态成员。
  2. 移除非静态成员与构造函数:不再需要myStructArray成员和构造函数,所有数据访问直接通过派生类的静态数据集完成。
  3. 静态方法访问数据集:派生类提供getDataSet()静态方法,返回编译期初始化的constexpr数组,基类的getData静态方法直接调用该方法获取数据。
  4. 编译期安全性:所有数据集均为constexpr,确保数据在编译期完全确定,运行时无额外开销。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 16:34:52