1

I am trying to use an initializer list for a data structure which inherits from its parents friend class's subclass. Below I compiled an example which demonstrates the problem(in 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;
}

The declaration is fine, but the initialization isn't, it gives the error could not convert {...} from '<brace-enclosed initializer list>' to i_gossip::i_am_secret secret. It is possible to compile the program by adressing and setting each induvidual members of the struct by so:

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

If the members are available to use, why does an initialization-list fails with a compile error?

Dávid Tóth
  • 2,788
  • 1
  • 21
  • 46
  • Cannot reproduce with clang++ (that give me only a warning, suggesting another brace level, `i_gossip::i_am_secret secret = {{0, {0,1,2}}};`) and with g++ (no error and no warning). Which compiler are you using? – max66 Sep 18 '19 at 09:19
  • gcc version 9.2.0 (Rev2, Built by MSYS2 project) with -std=c++11 – Dávid Tóth Sep 18 '19 at 09:20
  • 1
    Sorry: I've compiled C++17; yes: with C++11 and C++14 I reproduce your error. – max66 Sep 18 '19 at 09:20

1 Answers1

4

The two lines are not equivalent, despite the same identifier appearing in both. This one

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

assigns to the member variable whose type is secret_t. It just so happens that secret_t is an aggregate in C++11, so what it does is perform aggregate initialization of a temporary secret_t that gets assigned to i_have_secrets::i_am_secret.

On the other hand, this line

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

Attempts to initialize an object of type i_gossip::i_am_secret (not secret_t). In C++11, a class that has any base classes is not an aggregate. So the attempt at initializing a non-aggregate via aggregate initialization won't work.

You can use a type alias instead of a derived class

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

This will expose the inner type, aggregate initialization and all.

Alternatively, you can switch to C++17, where an aggregate is allowed to have public base classes.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458