Reputation: 1396
This question pertains to both array initialization and SystemC module hierarchies.
I have a class that is non-copyable, non-movable, and no default constructor:
class Object : public sc_module {
public:
Object(string name, Configuration config);
Object(const CPU& that) = delete;
};
and another class that has an array of these:
class Object2 : public sc_module {
public:
std::array<Object, 2> obj_array;
Object2() : obj_array <--??-->
{}
};
I need to initialize obj_array
with Objects
, which can't be copied and can't be moved.
I've tried a whole bunch of combinations and the one thing that compiles is:
class Object2: {
public:
std::array<Object, 2> obj_array;
//note the double braces on this initializer list
Object2() : obj_array {{ {"string1", config}, {"string2", config} }}
{}
};
It works, but I see some weird behavior later on in the code: when I print out the names of the Objects
in obj_array
, the first one is correct Object2.string1
but the next one is really weird Object2.string1.string2
when it should just be Object2.string2
).
I know a lot of people have asked pretty similar questions, but I'm trying to figure out exactly what's going on here. It also seems to me like I've got one too many braces in that initializer list, but it won't compile otherwise.
Am using g++ 6.4.0 with flag -std=c++14
The thing is if I create another array in the body of the constructor, eg class Object2: { public: std::array obj_array; //note the double braces on this initializer list Object2() : obj_array {{ {"string1", config}, {"string2", config} }} { Obj arr[2] {{"test", config}, {"test2", config}}; } };
Everything appears OK. arr
Object names are correct, as are their parents.
I know this is an esoteric question, but I need help understanding exactly what mechanisms are going on during my obj_array initialization.
Upvotes: 1
Views: 507
Reputation: 72401
The form with doubled braces is the "complete" way to initialize a std::array
, because std::array
is actually an aggregate struct which contains a raw C-style array. It's essentially
namespace std {
template <class T, size_t N>
struct array {
T __some_internal_name[N];
// public members...
};
}
So an initialization like:
std::array<int, 3> A{{ 2, 3, 4 }};
is valid because the outer {}
are for the std::array
struct object and the inner {}
are for the raw C-style array it contains.
But usually, you don't need the doubled braces, because there's a rule that lets you omit some nested braces for an aggregate inside another aggregate. This is why an initialization like:
std::array<int, 3> A{ 2, 3, 4 };
does the same as the above.
The rule is that as the compiler associates pieces of an initializer-list to subobjects (non-static data members of a struct/class or elements of an array) of an aggregate, if the next subobject is also an aggregate (which contains at least one subobject of its own) and the next piece of the initializer-list does not begin with the {
token, then the subobject takes as many pieces from the initializer-list as it has direct subobjects of its own. But if the next thing in the initializer-list begins with a {
, it is assumed to be the complete initializer-list for that aggregate subobject.
So if you have the almost-correct code
Object2() : obj_array { {"string1", config}, {"string2", config} }
{}
the compiler processes it something like this:
obj_array
is a std::array<Object, 2>
, which is an aggregate struct, and its initializer is a braced initializer-list, so aggregate initialization applies.
The first {
is associated with the obj_array
object.
obj_array
has one subobject, of type Object[2]
. This type is also an aggregate type, and the next thing in the initializer-list starts with {
, so it opens another initializer-list for initializing the Object[2]
array.
The Object[2]
contains two subobjects, both of type Object
, which is a non-aggregate class type.
The first Object
must be initialized with the next thing in the initializer-list, which is "string1"
. But there is no valid conversion from const char[8]
to Object
. This is an error.
Other errors might follow after that, depending on how the compiler attempts to continue.
In short, the reason the single-brace style for the std::array
doesn't work in your case is that since what you intended to be an Object
initializer starts with a {
, it gets treated as the initializer for the C-style array instead.
The double-brace solution works, or you could also specify the Object
type to avoid starting initializer-list elements with another initializer-list:
Object2() : obj_array { Object{"string1", config}, Object{"string2", config} }
{}
Your SystemC problem is not likely related to all this, since the double-brace style is correct and will do what you expect. It might be a detail of the Configuration
class. (Note that your constructor takes the Configuration
parameter "by value", meaning the object the constructor gets will be a copy of whatever you pass to it, if that matters.) I'd suggest asking a separate question with more details about that problem.
Upvotes: 3