【问题标题】:Initialise array of struct with const fields which don't have a default constructor使用没有默认构造函数的 const 字段初始化结构数组
【发布时间】:2018-04-23 08:56:20
【问题描述】:

我想定义一个结构数组,但这不起作用,因为它有一个没有默认构造函数的 const 字段。

Struct 是 SDK 的一部分,如下所示:

struct SParametricParamDef
{
    const TXString          fUniversalName;
    SResString              fLocalizedName;
    const TXString          fDefaultImperial;
    const TXString          fDefaultMetric;
    EFieldStyle             fFieldStyle;
    short                   fChoicesChcID;
};

TXString 没有默认构造函数。所以以下失败了:

SParametricParamDef *arrParams = new SParametricParamDef[size]; // <= throws compile time exception
for (int i = 0; i < size; i++)
{
    arrParams[i] = params[i].def; // <= also throws an exception, because operator = is not available
}

有没有办法解决这个问题?结果我需要一个SParametricParamDef*,因为这个数组又在SDK中使用了......

信息

在旧的 SDK 版本中,const TXSTringconst char*,那时我没有问题...现在我需要调整我的代码以使用新结构...

【问题讨论】:

  • 首先称为编译错误。其次,使用std::vector。如果您必须有一个指针,请从std::vector 获取它
  • 不知道这是可能的。我读到std::array 无法做到这一点,但std::vector 似乎可以使用数据字段。
  • std::array 不能实现什么?无论哪种方式,std::array 都不是你想要的
  • 我搞混了,抱歉。您建议使用std::vector 并使用std::vector::data 来获取底层指针,对吗?我现在不确定,如果这是保存,我可以从函数中返回这个指针还是会超出范围?因为这是我需要的,这就是为什么我在示例代码中使用new...
  • 传递std:: vector 并仅在需要时获取指针。了解 RAII 以及 c++ 如何管理内存,new 几乎总是一个错误

标签: c++ arrays struct constants


【解决方案1】:

这看起来像是一个使用 placement new 的示例:

SParametricParamDef *arrParams = (SParametricParamDef *) new char[size * sizeof(*arrParams)];

for (int i = 0; i < size; i++)
{
    // constructs an object in a pre-allocated memory
    new(arrParams+1) SParametricParamDef(params[i].def);
}

如果析构函数不重要,则应在释放数组之前显式调用它:

for (int i = 0; i < size; i++)
{
    ~SParametricParamDef(arrParams+1);
}
delete[] ((char *) arrParams);

这是相当老式的,因为它模仿 C 中结构的初始化,但它是我知道构建一个只有非平凡构造函数的对象数组的唯一方法(AFAIK,std::vector 方式需要可复制或可移动对象)

【讨论】:

  • 从技术上讲,这个答案是正确的,但应该使用 std::vector,它在内部也将使用placement-new。
  • @Jodocus:我同意这不是很地道,我只在绝望的用例中使用它,但它可以帮助处理奇怪的类。
  • 这似乎是解决我的问题的唯一方法。只是为了确保结果数组将能够被一个函数传回,并且只要我不手动破坏它就会一直存在(这是我所需要的,SDK 将保留这个数组,直到它破坏我的插件)如果我将 std::vector::data 指针传递给 SDK,这不会是真的,对吧?
  • @prom85: std::vector 使用起来可能更简单,你可以让它保持活动状态,例如,如果你可以在 main 中声明它,或者如果你只需要一个单人。这种方式较低级别,但仅取决于正确销毁数组指针。
【解决方案2】:

你得到的错误主要不是关于operator =,而是你默认构造了一个带有const成员的对象。这将使它们不可变,并且任何修改它们的尝试,正如您在循环中尝试的那样,都必须失败。 幸运的是,您可以使用emplace_back 直接在向量内初始化 SParametricParamDef 对象,而无需采用默认构造和赋值的间接方式:

std::vector<SParametricParamDef> arrParams;
for(std::size_t n = 0; n < size; ++n) {
    arrParams.emplace_back(params[n].def);
}

这应该最大限度地减少复制量,并且无需修改 struct 定义。

【讨论】:

    【解决方案3】:

    编译器告诉您,您要求创建一个TXString,而不指示如何初始化它。很难知道如何解决创建 TXString 对象的问题,因为您没有给出该类的构造函数列表,但就目前而言,需要对您给出的代码进行更改.解决这个问题的一些方法如下:

    最明显的是为SParametricParamDef 添加一个默认构造函数,它初始化TXString 对象:

    struct SParametricParamDef
    {
        SParametricParamDef() : fUniversalName(...), ... {}
    ...
    

    另一种方法,假设变量是const 可能是使它们const static

    为了简单起见,假设TXString 对象如下:

    struct TXString{
        TXString(char a) : _a(a) {}
        char _a;
    };
    

    然后您可以将SParametricParamDef 的声明更改为:

    struct SParametricParamDef
    {
        const static TXString fUniversalName;
    ...
    

    然后在你的实现文件中定义fUniversalName如下:

    const TXString SParametricParamDef::fUniversalName('D');
    

    另一种方法可能是将TXString 对象包装在另一个确实具有默认构造函数的对象中:

    struct TXStringWrapper {
        TXStringWrapper() : _s(...) {} // [1]
        const TXString& get() { return _s; }
    
    private:
        TXString _s;
    }
    

    在 [1] 处,您可以以任何您关心的特定、非默认方式创建 TXString

    【讨论】:

      猜你喜欢
      • 2011-06-17
      • 2020-01-18
      • 2021-12-16
      • 1970-01-01
      • 2011-06-12
      • 1970-01-01
      • 1970-01-01
      • 2011-06-22
      相关资源
      最近更新 更多