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?
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.