从朋友 class 继承时无法使用大括号括起来的初始值设定项列表

Unable to use brace enclosed initializer-list while inheriting from friend class

我正在尝试为从其父朋友 class 的子 class 继承的数据结构使用初始化列表。 下面我编译了一个演示问题的示例(在 c++11 中)。

#include <iostream>
#include <vector>

class i_gossip;

class i_have_secrets{
    friend class i_gossip;
public:
    i_have_secrets();
private:
    struct secret_t{
        int secret_number;
        std::vector<int> secret_vector;
    }i_am_secret;
};

class i_gossip{
public:
    struct i_am_secret : public i_have_secrets::secret_t { };
};

i_have_secrets::i_have_secrets(){
    i_am_secret = {0, {0,1,2}}; // Totally fine
}

int main(int argc,char** args){
    i_gossip::i_am_secret secret = {0, {0,1,2}}; // Compile error
    return 0;
}

声明没问题,初始化不行,报错could not convert {...} from '<brace-enclosed initializer list>' to i_gossip::i_am_secret secret。可以通过这样寻址和设置结构的每个成员来编译程序:

    i_gossip::i_am_secret secret;
    secret.secret_number = 0;
    secret.secret_vector = {0,1,2};

如果成员可用,为什么初始化列表会因编译错误而失败?

这两行并不等同,尽管两行中出现了相同的标识符。这个

i_have_secrets::i_have_secrets(){
    i_am_secret = {0, {0,1,2}}; // Totally fine
}

赋值给类型为secret_t成员变量。碰巧 secret_t 是 C++11 中的聚合,所以它所做的是执行 aggregate initialization 临时 secret_t 分配给 i_have_secrets::i_am_secret.

另一方面,这一行

int main(int argc,char** args){
    i_gossip::i_am_secret secret = {0, {0,1,2}}; // Compile error
    return 0;
}

尝试初始化类型为 i_gossip::i_am_secret(而非 secret_t)的对象。在 C++11 中,具有任何基数 class 的 class 不是聚合。因此,通过聚合初始化来初始化非聚合的尝试是行不通的。

您可以使用类型别名而不是派生的 class

class i_gossip{
public:
    using i_am_secret = i_have_secrets::secret_t;
};

这将公开内部类型、聚合初始化和所有内容。

或者,您可以切换到 C++17,其中聚合允许具有 public base classes.