Uniform initialization of derived class with trivial ctor
Asked Answered
E

1

8

I'm trying to wrap my head around some corner cases with c++11 uniform initialization and I can't figure out why is this:

struct Base
{
    int x,y,z;
};

struct Derived : Base
{
};
static_assert (std::is_trivial<Base>::value, "Base must be trivial");
static_assert (std::is_trivial<Derived>::value, "Derived must be trivial");

Base b{1, 2, 3};           // 1) This compiles fine
Derived d{10, 20, 30};     // 2) This fails

Line marked 2 fails with a "no matching constructor for initialization of Derived" message both with clang 3.1 and g++ 4.7.

I can't understand why, in case of Derived, it is trying to call a constructor and not performing (I don't know how to call it, maybe aggregate initialization?) as is the case for line 1).

Something in the following reasoning is wrong?:

A) Being trivial guarantees it can be statically initialized

B) To be statically initialized no code must be executed at runtime and hence no constructor call is required A+B => why is it trying to call a constructor on a type that it knows being trivial?

I'm very confused....

Edmanda answered 29/11, 2012 at 12:23 Comment(0)
N
11

Being trivial has nothing to do with how you can initialize something. The important bit is whether your Derived type is an aggregate, which is not the case:

§8.5.1 [dcl.init.aggr] p1

An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no brace-or-equal-initializers for non-static data members (9.2), no private or protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).

Only aggregates can be initialized with aggregate initialization, so list-initialization (which is the official name for uniform initialization) can only try looking for a fitting constructor.

What you can do is provide a constexpr constructor that forwards to the base-class, and add a defaulted default constructor:

struct Derived : Base{
    Derived() = default;
    constexpr Derived(int a, int b, int c) : Base{a, b, c}{}
};
Nibble answered 29/11, 2012 at 12:41 Comment(11)
Thanks for the pointer to the relevant part of the standard. That certainly explains what's wrong with my code (and assumptions). But so what's the correct way to interpret something like (see en.wikipedia.org/wiki/C++11) "A type that is trivial can be statically initialized"? How one can statically initialize something "trivial" when that something is a derived class and hence per 8.5.1 requires calling a constructor?Edmanda
@abigagli: by having the constructor be constexpr.Wessels
@Xeo: not in this case, there just is a confusion of concepts.Wessels
@Matthieu: Right.Nibble
Matthieu is right here, the confusion is probably around what static initialization means, which is completely unrelated to aggregate initializationRosannarosanne
@David, you're right. I had a bug in my concepts. Would it be right to say (probably a bit oversimplifying) that static initialization has to do with not executing runtime code while aggregate initialization has just to do with initializing using curlies? If that's correct, what role does triviality play here? Maybe is the necessary condition to enable static initialization potentially using constexpr?Edmanda
@abigagli: static initialization does mean that no function needs to be called at runtime, the main concern here is initialization of objects with static storage duration, which is performed in two phases within each TU, first all static initialization, then all dynamic initialization in the order of definition of the variables. Note that a trivial type still has a constructor, although one that performs no operations. Aggregate initialization is the ability to set the fields of an aggregate directly at the place of definition of the variable without the existence of a constructor[...]Rosannarosanne
[...] struct X { int a,b; }; has trivial default and copy constructors. It does not have any constructor that takes 1 or 2 integers, but because it is an aggregate you can set the values at the place of definition of a variable: X x = { 1,2 };. Note that I am avoiding the curly-brace intentionally for two reasons: the syntax is less important than the feature and in C++11 that syntax can be used to call a constructor.Rosannarosanne
@David: I think you're missing a "not" in that first part. Also, in C++11 T v = { ... }; can also be list-initialization, it doesn't have to be T v{ ... };.Nibble
@Xeo: Where do you think I am missing the 'not' (a quick read did not pop into mind). Regarding the curly braces I tried to avoid using that term as in C++11 it has more meanings than aggregate initialization, as you point out.Rosannarosanne
@David: Ah, you were speaking about the term itself. For the "not", I may have misread. The first sentence sounds a bit weird, but it seems you're confirming the OP's assumption in his last comment.Nibble

© 2022 - 2024 — McMap. All rights reserved.