繁体   English   中英

带指示符的派生 class 的聚合初始化

[英]Aggregate initialization of a derived class with designator

cppreference我们可以知道:

C++17以来派生类型:

若初始化器子句是嵌套的花括号初始化列表(不是表达式),则对应的数组元素/类成员/公共基础(C++17 起)从该子句列表初始化:聚合初始化是递归的。

C++20以来聚合类型:

使用.designator = arg指定初始化成员不再是对C 的扩展,而是C++ 标准的支持。

struct A
{
    int a;
    int b;
};

struct B : A
{
    int c;
};

void test()
{
    A a{.a = 1, .b = 42}; // a.a = 1 && a.b = 42
    B b{{.a = 1, .b = 2}, 42}; // b.a = 1 && b.b = 2 && b.c = 42
}

非常简单直观,对吧?

但是让我们把事情复杂化一点。

constexpr int a_very_very_special_value = 42;

struct A
{
    int a;
    int b;
};

struct B : A
{
    int c = a_very_very_special_value;
    int d;
};

void test()
{
    A a{.a = 1, .b = 42}; // a.a = 1 && a.b = 42
    B b{{.a = 1, .b = 2}, .d = 9}; // Ouch! Mixture of designated and non-designated initializers in the same initializer list is a C99 extension.
}

上面的情况应该是很常见的,我们希望一个成员数据保持默认值,但是标准要求我们必须.designator = arg之后所有的参数都没有.designator ,但是,这是不可能的,我们怎么给我们的base一个名字? .base_type_name = {.a = 1, .b = 2}吗?

有人可能会认为inheritance这样的东西完全没有必要,我们可以这样做。

struct A
{
    int a;
    int b;
};

struct B
{
    A base;
    int c = a_very_very_special_value;
    int d;
};

void test()
{
    A a{.a = 1, .b = 42}; // a.a = 1 && a.b = 42
    B b{.base = {.a = 1, .b = 2}, .d = 9}; // b.base.a = 1 && b.base.b = 2 && b.c = a_very_very_special_value && b.d = 9
}

太好了,还有什么不满意的? 除非我们有这样的界面。

void test(A &a)
{
    if (a.a + a.b > 42) {
        std::cout << "42\n";
    }
    else {
        std::cout << "0\n";
    }
}

void test()
{
    A a{.a = 1, .b = 42};
    B b{.base = {.a = 1, .b = 2}, .d = 9};

    test(a);
    test(b); // No matching function for call to 'test'
}

熟悉memory model的肯定会觉得很简单,加个强制类型转换就可以了,反正原来base class的数据在数据头部。

void test()
{
    A a{.a = 1, .b = 42};
    B b{.base = {.a = 1, .b = 2}, .d = 9};

    test(a);
//  test(b);
    test((A&)b);
}

但是,很重要的一点是C++支持多个inheritance,如果我们最初的设计思路是通过继承多个结构体形成一个完整的类型,那怎么办呢?

更进一步,如果我们的接口对于每个继承的类型都是独立的,那么我们需要给所有基类一个类型转换方法,并且还要知道数据在类型中的偏移量,如果来自基类class的数据没有完全放在class的团长,结果绝对是错误的。

正如您所发现的,您不能既使用指定的初始化又将基数命名为 class。为此,需要扩展语言以支持这种情况。 P2287R1 ,我还没有完成。 不幸的是,不会制作 C++23。

在语言直接支持指定基类 class(或直接指定基类的成员)之前,如果你想使用指定的初始化器,你最好的选择是将基类作为成员,正如你已经明确记录的那样。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM