【问题标题】:How to use aggregated initialization in derived class with CRTP pattern?如何在具有 CRTP 模式的派生类中使用聚合初始化?
【发布时间】:2020-10-27 15:54:59
【问题描述】:

我使用的标准是c++17

问题是我想重新制作我的代码并使用 CRPT 重新组织我的代码结构,因为它非常适合,但问题是以前的代码在类中使用聚合初始化,例如:

ClassWithNoParent get(const T_& unit) const {
        return {.w = 1,
                .h = 1,
                .c = 1,
                .n = 1};
    }

没关系。当我使用 CRTP 时

class DefaultClass {
 public:
    int n;
    int c;
    int h;
    int w;
};

template <class SuccessorT, class T = DefaultClass>
class BaseCRTPClass {
 public:
    int w;
    int h;
    int c;
    int n;

    SuccessorT get(const DefaultClass&) const {
        return   {.w = 1,
                  .h = 1,
                  .c = 1,
                  .n = 1};
    }
};

class Successor : public BaseCRTPClass<Successor> {};

int main(){
    Successor t;
    auto k = t.get(DefaultClass{});
}

编译失败并报错

21:25: error: could not convert '{1, 1, 1, 1}' from '<brace-enclosed initializer list>' to 'Successor'

这是预期的,因为标准希望 Successor 被聚合,但是我不太确定 c++17 严格禁止没有基类。它限制了构造函数(据我了解,但我可能错了)。那么,我怎样才能绕过这个问题呢?

如何为 CRTP 定义的派生类保留聚合初始化?

附:为什么要保留聚合初始化?因为我的代码中很多地方都使用了这种初始化,但是如果在 CRTP 中重新制作,那么一切都会被粉碎,我将不得不替换某些构造函数上的所有聚合初始化......

【问题讨论】:

  • Successor 已经是一个聚合。这不是问题的来源。我不确定该怎么做,但请查看this。显然指定 SuccessorT 并删除第一个参数的指定名称会使代码编译。

标签: c++ c++17 crtp


【解决方案1】:

该问题与 CRTP 无关。派生类的聚合初始化仅在 C++17 中引入。要聚合初始化派生类,必须将基类初始化为第一项,在其自己的初始化列表中。因此,要使其正常工作,请使用双括号:

return {{.w = 1, .h = 1, .c = 1, .n = 1}};

为了说明,假设您有一个更简单的非模板类:

struct Base { int a; };
struct Derived : public Base { int b; };

你必须将Base初始化为第一项:

Derived x = { 
    { .a = 42 }, // Base
    .b = 24      // b
};

【讨论】:

  • 指定成员初始值设定项是 C++20 的一项功能,不是核心 C++17 语言标准的一部分(但许多编译器提供作为扩展)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-10
  • 2017-03-24
  • 1970-01-01
  • 2019-04-13
  • 2019-12-29
相关资源
最近更新 更多