Равномерная инициализация производного класса с тривиальным ctor

Я пытаюсь обдумать некоторые угловые случаи с униформной инициализацией С++ 11, и я не могу понять, почему это так:

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

Строка с пометкой 2 завершается ошибкой с сообщением "нет подходящего конструктора для инициализации Derived" как с clang 3.1, так и с g++ 4.7.

Я не могу понять, почему в случае Derived он пытается вызвать конструктор и не выполняет (я не знаю, как это назвать, может быть, агрегатная инициализация?), как в случае с линия 1).

Что-то в следующих рассуждениях неверно?:

A) Тривиальность гарантирует, что он может быть статически инициализирован

B) Для статической инициализации никакой код не должен выполняться во время выполнения, и, следовательно, не требуется вызов конструктора A+B => почему он пытается вызвать конструктор для типа, который, как он знает, является тривиальным?

я очень запуталась....


person abigagli    schedule 29.11.2012    source источник


Ответы (1)


Тривиальность не имеет ничего общего с тем, как вы можете что-то инициализировать. Важным моментом является то, является ли ваш тип Derived агрегатом, что не так:

§8.5.1 [dcl.init.aggr] p1

Агрегат – это массив или класс (раздел 9) без конструкторов, предоставляемых пользователем (12.1), без инициализаторов-или-равно-скобок для нестатических элементов данных. (9.2), никаких частных или защищенных нестатических элементов данных (пункт 11), никаких базовых классов (пункт 10) и никаких виртуальных функций (10.3).

Только агрегаты могут быть инициализированы агрегатной инициализацией, поэтому list-initialization (официальное название юниформ-инициализации) может только попытаться найти подходящий конструктор.

Что вы можете сделать, так это предоставить конструктор constexpr, который пересылает в базовый класс, и добавить конструктор по умолчанию defaulted:

struct Derived : Base{
    Derived() = default;
    constexpr Derived(int a, int b, int c) : Base{a, b, c}{}
};
person Xeo    schedule 29.11.2012
comment
Спасибо за ссылку на соответствующую часть стандарта. Это, безусловно, объясняет, что не так с моим кодом (и предположениями). Но как правильно интерпретировать что-то вроде (см. en.wikipedia.org/wiki/C ++11) Можно ли статически инициализировать тривиальный тип? Как можно статически инициализировать что-то тривиальное, если это что-то является производным классом и, следовательно, согласно 8.5.1 требует вызова конструктора? - person abigagli; 29.11.2012
comment
@abigagli: если конструктор будет constexpr. - person Matthieu M.; 29.11.2012
comment
@Xeo: не в этом случае, просто путаница понятий. - person Matthieu M.; 29.11.2012
comment
@Matthieu: Правильно. - person Xeo; 29.11.2012
comment
Матье прав, путаница, вероятно, связана с тем, что означает статическая инициализация, что совершенно не связано с агрегатной инициализацией. - person David Rodríguez - dribeas; 29.11.2012
comment
@ Дэвид, ты прав. У меня была ошибка в моих понятиях. Было бы правильно сказать (вероятно, немного упрощая), что статическая инициализация связана с отсутствием выполнения кода среды выполнения, в то время как агрегатная инициализация связана только с инициализацией с использованием фигурных скобок? Если это так, то какую роль здесь играет тривиальность? Может быть, это необходимое условие для включения статической инициализации с использованием constexpr? - person abigagli; 29.11.2012
comment
@abigagli: статическая инициализация означает, что во время выполнения не нужно вызывать никаких функций, основная проблема здесь — инициализация объектов со статической продолжительностью хранения, которая выполняется в два этапа в каждой TU, сначала все < i>статическая инициализация, затем все динамическая инициализация в порядке определения переменных. Обратите внимание, что у тривиального типа по-прежнему есть конструктор, хотя он и не выполняет никаких операций. Агрегатная инициализация — это возможность устанавливать поля агрегата непосредственно в месте определения переменной без существования конструктора[...] - person David Rodríguez - dribeas; 29.11.2012
comment
[...] struct X { int a,b; }; имеет тривиальные конструкторы default и copy. У него нет конструктора, который принимает 1 или 2 целых числа, но поскольку это агрегат, вы можете установить значения в месте определения переменной: X x = { 1,2 };. Обратите внимание, что я намеренно избегаю фигурных скобок по двум причинам: синтаксис менее важен, чем функция, и в C++11 этот синтаксис можно использовать для вызова конструктора. - person David Rodríguez - dribeas; 29.11.2012
comment
@David: я думаю, что вы пропустили не в той первой части. Кроме того, в С++ 11 T v = { ... }; также может быть инициализацией списка, это не обязательно должно быть T v{ ... };. - person Xeo; 29.11.2012
comment
@Xeo: Как вы думаете, где я пропустил «не» (быстрое чтение не пришло в голову). Что касается фигурных скобок, я старался избегать использования этого термина, поскольку в C++11 он имеет больше значений, чем агрегатная инициализация, как вы указываете. - person David Rodríguez - dribeas; 29.11.2012
comment
@David: Ах, ты говорил о самом термине. Для нет, я, возможно, неправильно прочитал. Первое предложение звучит немного странно, но, похоже, вы подтверждаете предположение ОП в его последнем комментарии. - person Xeo; 29.11.2012