Здравствуйте, rg45, Вы писали:
R>Ну, в таком случае очень хотелось бы найти объяснение.
R>Выглядит очень странно: вынос структуры наружу или отказ от инициализации мембера устраняют ошибку. Кто-нибудь видит связь или логику? Я — нет.
типа в этом случае вложенный класс считается неполным до завершения объявления внешнего
This declares a default constructor that might be defined implicitly by the compiler, **or** it might get deleted if the member definitions of D would make the implicit default constructor ill-formed. This is obviously very different from the case where you declare B(); There is no assertion that D is default constructible, the compiler has to deduce whether or not that's true. That depends on the default member initializer for D::i. The initializer (which is just '0' here) is a "complete class context" which means it is not processed until the class D is complete (this allows you to use other members, or e.g. sizeof(D) as the initializer).
A nested class like C::D is not complete until its enclosing class is complete. This means the initializer for C::D::i is compiled after C is complete. This means whether C::D is default constructible is not known until C is complete.
Здравствуйте, sergii.p, Вы писали:
SP>Здравствуйте, rg45, Вы писали:
R>>Вот если бы ещё получить ссылку на соответствующий пункт стандарта, было бы совсем здорово.
SP>[class.mem.general]
SP>
SP>[Note 4 : A complete-class context of a nested class is also a complete-class context of any enclosing class, if the nested
SP>class is defined within the member-specification of the enclosing class. — end note]
SP>7 A class is considered a completely-defined object type (6.8) (or complete type) at the closing } of the
SP>class-specifier. The class is regarded as complete within its complete-class contexts; otherwise it is regarded
SP>as incomplete within its own class member-specification.
о! нашел таки на llvm
работают над исправлением как я понял,но вяло
Здравствуйте, rg45, Вы писали:
R>Здравствуйте, sergii.p, Вы писали:
R>Имхо, самое важное в этом беспорядочном потоке сознания заключено вот в этом коротеньком утверждении:
R>
R>A nested class like C::D is not complete until its enclosing class is complete.
R>Вот если бы ещё получить ссылку на соответствующий пункт стандарта, было бы совсем здорово.
Здравствуйте, rg45, Вы писали:
R>Вот если бы ещё получить ссылку на соответствующий пункт стандарта, было бы совсем здорово.
[class.mem.general]
[Note 4 : A complete-class context of a nested class is also a complete-class context of any enclosing class, if the nested
class is defined within the member-specification of the enclosing class. — end note]
7 A class is considered a completely-defined object type (6.8) (or complete type) at the closing } of the
class-specifier. The class is regarded as complete within its complete-class contexts; otherwise it is regarded
as incomplete within its own class member-specification.
ну так и в этом тикете такая же лапша. "Классы, контексты какие-то. Взять всё, и починить!". А меж тем тикету 3 года уже. Думаю разработчики сами плохо понимают аргументацию в стандарте, но это хорошая отмазка не чинить
Здравствуйте, B0FEE664, Вы писали:
BFE>Почему некоторые компиляторы это отказываются компилировать ?
Реализация variant-ов кривая
Добавь туда пару типов std::variant<bool,FService> data_;
или пустой конструктор FService::FService() {}
и заработает
Здравствуйте, Marty, Вы писали:
M>Здравствуйте, B0FEE664, Вы писали:
BFE>>Почему некоторые компиляторы это отказываются компилировать ?
M>Лучше соответствуют стандарту?
Если убрать инициализацию (int nFService_ = 0; ) то компилируется.
Такая инициализация перестала быть стандартной?
BFE>>using value_type = float;
BFE>>struct A
BFE>>{
BFE>> struct B
BFE>> {
BFE>> value_type n = 0;
BFE>> };
BFE>> using value_type = int;
BFE>>};
BFE>>
R>Не понял я что ты хотел сказать этим примером.
Имя. Где искать имя?
Почему при декларации используется один порядок поиска имён, а при вызове — другой?
Здравствуйте, rg45, Вы писали:
R>Здравствуйте, Великий Мессия, Вы писали:
ВМ>>в так в GCC тоже самое ВМ>>сомневаюсь что два мажорный компиля синхронно не правы
R>Ну, в таком случае очень хотелось бы найти объяснение.
R>Выглядит очень странно: вынос структуры наружу или отказ от инициализации мембера устраняют ошибку. Кто-нибудь видит связь или логику? Я — нет.
согласен
если бы не засветили здесь эту проблему
я бы сходил в issue кланга или на багзиллу gcc
задал вопросик
по issues кланга я похожих кейсов не нашел
так что, велком кто то зарепортите
и оставьте линк сюда
для мониторинга
R>struct D
R>{
R> struct FService
R> {
R> int nFService_ = 0;
R> };
R> static_assert(sizeof(FService) != 0); // OK
// не является complete-class context так как не является ни одним из перечисленных вариантов в 10.1 - 10.6 отсюда https://timsong-cpp.github.io/cppwp/class.mem.general#10
R> static_assert(std::default_initializable<FService>); // static assertion failed
// тут complete-class context это инициализирующее выражение "0" для FService::nFService_ (https://timsong-cpp.github.io/cppwp/class.mem.general#10.6), размещенное "within the member-specification", коим является static_assert (https://timsong-cpp.github.io/cppwp/class.mem.general#nt:member-declaration)
// этот complete-class context провоцирует дефолтный конструктор FService к удалению, тест default_initializable проваливается, получаем ложь и провал static_assert
R>};
R>
R>Выходит, класс "достаточно полный" для sizeof, но недостаточно полный для того, чтобы понять, что он default infitializable. Это как так?
R>>struct D
R>>{
R>> struct FService
R>> {
R>> int nFService_ = 0;
R>> };
R>> static_assert(sizeof(FService) != 0); // OK
V>// не является complete-class context так как не является ни одним из перечисленных вариантов в 10.1 - 10.6 отсюда https://timsong-cpp.github.io/cppwp/class.mem.general#10
R>> static_assert(std::default_initializable<FService>); // static assertion failed
V>// тут complete-class context это инициализирующее выражение "0" для FService::nFService_ (https://timsong-cpp.github.io/cppwp/class.mem.general#10.6), размещенное "within the member-specification", коим является static_assert (https://timsong-cpp.github.io/cppwp/class.mem.general#nt:member-declaration)
V>// этот complete-class context провоцирует дефолтный конструктор FService к удалению, тест default_initializable проваливается, получаем ложь и провал static_assert
R>>};
R>>
Примерно понял, спасибо. Почитаю ещё документ повнимательнее.
PS. Хотя логика этих зависимостей мне не очень понятна. Получается, что в обрамляющем классе можно написать нечто такое, отчего вложенный класс не сможет быть default initializable. В противном случае зачем эти сложности.
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, B0FEE664, Вы писали:
BFE>Здравствуйте, rg45, Вы писали:
R>>Ну вот, а мне тут про какие-то контексты лапшу на уши навешивают
BFE>
BFE>using value_type = float;
BFE>struct A
BFE>{
BFE> struct B
BFE> {
BFE> value_type n = 0;
BFE> };
BFE> using value_type = int;
BFE>};
BFE>
только немного не так. Здесь value_type будет как раз float.
using value_t = float;
struct Outer {
struct Inner {
value_t i;
};
std::variant<Inner> v; // value_t = float, Inner - считается полным, конструктор создать можно, код компилируетсяusing value_t = long long; // это переопределение уже никого не волнует, тип Inner готов
};
а вот как раз с дефолтной инициализацией компилятор не знает откуда взять определение
static constexpr int SuperDuperConst = 0;
struct Outer {
struct Inner {
int i = SuperDuperConst;
};
std::variant<Inner> v; // i должна быть равна 42, но мы ещё не добрались до определения SuperDuperConst и конструктор сгенерировать не можем - компиляция провалиласьstatic constexpr int SuperDuperConst = 42;
};
SP>static constexpr int SuperDuperConst = 0;
SP>struct Outer {
SP> struct Inner {
SP> int i = SuperDuperConst;
SP> };
SP> std::variant<Inner> v; // i должна быть равна 42, но мы ещё не добрались до определения SuperDuperConst и конструктор сгенерировать не можем - компиляция провалилась
SP> static constexpr int SuperDuperConst = 42;
SP>};
SP>
А когда вместо SuperDuperConst используется литеральный ноль в качестве инициализатора, куда мы в этом случае ещё не добрались?
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, sergii.p, Вы писали:
SP>только немного не так. Здесь value_type будет как раз float. SP>а вот как раз с дефолтной инициализацией компилятор не знает откуда взять определение
То есть вы считаете, что поиск по имени константы и по имени типа является (и должен быть) различным? (как правильно я не разбирался)
Дело в том, что вот такой код:
using value_type = float;
struct O
{
struct I
{
value_type n = 2.3;
};
using value_type = int;
};
int main()
{
O::I in;
std::cout << in.n;
return 0;
}
gcc не компилирует error: declaration of 'using O::value_type = int' changes meaning of 'value_type' [-Wchanges-meaning]
39 | using value_type = int;
| ^~~~~~~~~~
<source>:37:6: note: used here to mean 'using value_type = float'
37 | value_type n = 2.3;
| ^~~~~~~~~~
<source>:31:7: note: declared here
31 | using value_type = float;
код можно немного усложнить (с тем же эффектом: gcc: error, clanc: n — это float):
int f(int)
{
return 0;
}
//template<class T = char> struct A
{
int g() { return f(0); } // здесь f - член класса using value_Type = decltype(f(0)); // здесь f - внешняя функция?struct B
{
value_Type n = 1.1;
} b;
float f(int) { return 0;}
};
По-моему, в стандарте явно не хватает разделения правил для инициализаторов, зависящих и не зависящих от контекста обрамляющего класса. Типа как существуют различные правила для выражений зависящих и не зависящих от параметров шаблона. Ну, провтыкали они этот момент, бывает.
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, B0FEE664, Вы писали:
BFE>То есть вы считаете, что поиск по имени константы и по имени типа является (и должен быть) различным? (как правильно я не разбирался)
Там с этим unqualified name lookup сам чёрт лапы поломает. Но по факту выходит что да, правила разные.
--
Справедливость выше закона. А человечность выше справедливости.