Ошибка компиляции при использовании пустого конструктора инициализации списка в C ++ 17 - PullRequest
0 голосов
/ 08 января 2019

Я столкнулся со странной проблемой при попытке перейти на C ++ 17. Проблема в том, что кое-что (и я не уверен, что) изменилось в C ++ 17, что заставило инициализацию списка работать по-другому в случае конструктора по умолчанию. Я пытался найти https://en.cppreference.com/w/cpp/language/list_initialization для получения дополнительной информации, но я не нашел ничего, что выглядит уместным.

Кто-нибудь знает причину, по которой код ниже компилируется в C ++ 14, но не в C ++ 17 при вызове B{} вместо B()? (Я пробовал это в gcc 8.2 и 7.3 и в icc 19)

struct A{
protected:
    A() {}
};

struct B : public A {};


B f(){
    return B(); //compilation OK
    //return B{}; //compilation error
}

Ответы [ 3 ]

0 голосов
/ 08 января 2019

Из моего понимания https://en.cppreference.com/w/cpp/language/value_initialization

B{} выполняет агрегирование_инициализации ,

и с C ++ 17:

Эффекты инициализации агрегата:

  • Каждая прямая открытая база (начиная с C ++ 17) [..] инициализируется копией из соответствующего предложения списка инициализаторов.

и в нашем случае:

Если количество предложений инициализатора меньше количества членов и баз (начиная с C ++ 17) или список инициализаторов полностью пуст, остальные члены и базы (начиная с C ++ 17) инициализируются инициализаторами по умолчанию , если предусмотрено в определении класса, и в противном случае (начиная с C ++ 14) пустыми списками, в соответствии с обычными правилами инициализации списков (которые выполняют инициализацию значений для не-классов типов и неагрегированных классов с конструкторами по умолчанию, и инициализация агрегатов для агрегатов). Если член ссылочного типа является одним из этих оставшихся членов, программа является некорректной.

Так B{/*constructor of A*/} нужно построить базовый класс А, который защищен ...

0 голосов
/ 08 января 2019

В окончательном варианте C ++ 17 n4659 есть раздел совместимости, в котором содержатся изменения относительно предыдущих версий.

C.4.4 Пункт 11: деклараторы [diff.cpp14.decl]

11.6.1
Изменение: определение агрегата расширено для применения к пользовательским типам с базовыми классами.
Обоснование: увеличить удобство инициализации агрегата.
Эффект на оригинальную особенность: Действительный код C ++ 2014 может не скомпилироваться или привести к другим результатам в этом международном стандарте; инициализация из пустого список инициализатора выполнит агрегатную инициализацию вместо вызов конструктора по умолчанию для затронутых типов:

struct derived;
struct base {
friend struct derived;
private:
base();
};
struct derived : base {};
derived d1{}; // Error. The code was well-formed before.
derived d2; // still OK

Я скомпилировал приведенный выше пример кода с помощью -std=c++14, и он скомпилирован, но не скомпилирован с помощью -std=c++17.

Я полагаю, что это может быть причиной того, что код в OP не работает с B{}, но успешно с B().

0 голосов
/ 08 января 2019

В C ++ 14 определение совокупности было:

Агрегат - это массив или класс (Clause [class]) без предоставленных пользователем конструкторов ([class.ctor]), без закрытых или защищенных нестатических элементов данных (Clause [class.access]), без базовых классов (пункт [class.derived]) и без виртуальных функций ([class.virtual]).

Следовательно, B не является совокупностью. В результате B{}, безусловно, не является агрегатной инициализацией, а B{} и B() означают одно и то же. Они оба просто вызывают конструктор B по умолчанию.

Однако в C ++ 17 определение совокупности было изменено на:

агрегат - это массив или класс с

  • нет пользовательских, явных или унаследованных конструкторов ([class.ctor]),
  • нет личных или защищенных нестатических элементов данных (пункт [class.access]),
  • без виртуальных функций и
  • нет виртуальных, частных или защищенных базовых классов ([class.mi]).

[Примечание: Совокупная инициализация не позволяет получить доступ к защищенным и закрытым членам или конструкторам базового класса. - конечная нота]

Ограничение больше не распространяется на любых базовых классов, а только на виртуальные / частные / защищенные. Но B имеет публичный базовый класс. Теперь это совокупность! И агрегатная инициализация C ++ 17 позволяет инициализировать подобъекты базового класса.

В частности, B{} - это агрегатная инициализация, в которой мы просто не предоставляем инициализатор для какого-либо подобъекта. Но первым (и единственным) подобъектом является A, который мы пытаемся инициализировать с {} (во время агрегатной инициализации любой подобъект без явного инициализатора инициализируется копией из {}), который мы можем ' Это происходит потому, что конструктор A защищен, а мы не друзья (см. также цитату).

<ч />

Обратите внимание, что для интереса в C ++ 20 определение совокупности *1059* снова изменится.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...