std::variant для вложенного класса
От: B0FEE664  
Дата: 03.12.25 20:05
Оценка: 11 (2) +1
Почему некоторые компиляторы это отказываются компилировать ?
#include <variant>

struct D
{
    struct FService
    {
        int  nFService_ = 0;
    };

    std::variant<FService> data_;
};

int main() 
{
D d;
return 0;
}


здесь
И каждый день — без права на ошибку...
Отредактировано 03.12.2025 20:06 B0FEE664 . Предыдущая версия .
Re: std::variant для вложенного класса
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 03.12.25 20:46
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Почему некоторые компиляторы это отказываются компилировать ?


Лучше соответствуют стандарту?


BFE>здесь


Там же вроде всё сказано?
Маньяк Робокряк колесит по городу
Re: std::variant для вложенного класса
От: kov_serg Россия  
Дата: 03.12.25 21:13
Оценка: -1
Здравствуйте, B0FEE664, Вы писали:

BFE>Почему некоторые компиляторы это отказываются компилировать ?

Реализация variant-ов кривая
Добавь туда пару типов std::variant<bool,FService> data_;
или пустой конструктор FService::FService() {}
и заработает
Re[2]: std::variant для вложенного класса
От: Doom100500 Израиль  
Дата: 04.12.25 06:42
Оценка: +1
Здравствуйте, Marty, Вы писали:

M>Здравствуйте, B0FEE664, Вы писали:


BFE>>Почему некоторые компиляторы это отказываются компилировать ?


M>Лучше соответствуют стандарту?


Если убрать инициализацию (int nFService_ = 0; ) то компилируется.
Такая инициализация перестала быть стандартной?
Спасибо за внимание
Отредактировано 04.12.2025 6:43 Doom100500 . Предыдущая версия . Еще …
Отредактировано 04.12.2025 6:42 Doom100500 . Предыдущая версия .
Re: std::variant для вложенного класса
От: rg45 СССР  
Дата: 04.12.25 08:41
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Почему некоторые компиляторы это отказываются компилировать ?

BFE>
BFE>#include <variant>

BFE>struct D
BFE>{
BFE>    struct FService
BFE>    {
BFE>        int  nFService_ = 0;
BFE>    };

BFE>    std::variant<FService> data_;
BFE>};

BFE>int main() 
BFE>{
BFE>D d;
BFE>return 0;
BFE>}
BFE>


BFE>здесь


Я склоняюсь к тому, что это баг компилера. По идее, вложенность структур здесь не должна иметь никакого значения, а имеет.
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 04.12.2025 8:42 rg45 . Предыдущая версия .
Re: std::variant для вложенного класса
От: rg45 СССР  
Дата: 04.12.25 09:37
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Почему некоторые компиляторы это отказываются компилировать ?


Я бы упростил постановку вопроса:

https://godbolt.org/z/P4do6chPo

#include <concepts>

struct D
{
    struct FService
    {
        int  nFService_ = 0;
    };

    static_assert(std::default_initializable<FService>); // error: static assertion failed
};


Какого Х?
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 04.12.2025 9:40 rg45 . Предыдущая версия .
Re[2]: std::variant для вложенного класса
От: Великий Мессия google
Дата: 04.12.25 10:50
Оценка:
в так в GCC тоже самое
сомневаюсь что два мажорный компиля синхронно не правы
Re[3]: std::variant для вложенного класса
От: rg45 СССР  
Дата: 04.12.25 11:10
Оценка:
Здравствуйте, Великий Мессия, Вы писали:

ВМ>в так в GCC тоже самое

ВМ>сомневаюсь что два мажорный компиля синхронно не правы

Ну, в таком случае очень хотелось бы найти объяснение.

Выглядит очень странно: вынос структуры наружу или отказ от инициализации мембера устраняют ошибку. Кто-нибудь видит связь или логику? Я — нет.
--
Справедливость выше закона. А человечность выше справедливости.
Re[4]: std::variant для вложенного класса
От: Великий Мессия google
Дата: 04.12.25 11:17
Оценка:
Здравствуйте, rg45, Вы писали:

R>Здравствуйте, Великий Мессия, Вы писали:


ВМ>>в так в GCC тоже самое

ВМ>>сомневаюсь что два мажорный компиля синхронно не правы

R>Ну, в таком случае очень хотелось бы найти объяснение.


R>Выглядит очень странно: вынос структуры наружу или отказ от инициализации мембера устраняют ошибку. Кто-нибудь видит связь или логику? Я — нет.


согласен
если бы не засветили здесь эту проблему
я бы сходил в issue кланга или на багзиллу gcc
задал вопросик

по issues кланга я похожих кейсов не нашел

так что, велком кто то зарепортите
и оставьте линк сюда
для мониторинга
Re[4]: std::variant для вложенного класса
От: sergii.p  
Дата: 04.12.25 14:17
Оценка: 10 (1) +2
Здравствуйте, rg45, Вы писали:

R>Ну, в таком случае очень хотелось бы найти объяснение.


R>Выглядит очень странно: вынос структуры наружу или отказ от инициализации мембера устраняют ошибку. Кто-нибудь видит связь или логику? Я — нет.


типа в этом случае вложенный класс считается неполным до завершения объявления внешнего

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88165

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.

Re[5]: std::variant для вложенного класса
От: rg45 СССР  
Дата: 04.12.25 14:38
Оценка:
Здравствуйте, sergii.p, Вы писали:

Имхо, самое важное в этом беспорядочном потоке сознания заключено вот в этом коротеньком утверждении:

A nested class like C::D is not complete until its enclosing class is complete.


Вот если бы ещё получить ссылку на соответствующий пункт стандарта, было бы совсем здорово.
--
Справедливость выше закона. А человечность выше справедливости.
Re[6]: std::variant для вложенного класса
От: vopl Россия  
Дата: 04.12.25 14:56
Оценка: 16 (1) +1
Здравствуйте, rg45, Вы писали:

R>Здравствуйте, sergii.p, Вы писали:


R>Имхо, самое важное в этом беспорядочном потоке сознания заключено вот в этом коротеньком утверждении:


R>

R>A nested class like C::D is not complete until its enclosing class is complete.


R>Вот если бы ещё получить ссылку на соответствующий пункт стандарта, было бы совсем здорово.


https://timsong-cpp.github.io/cppwp/class.mem.general#note-4
Re[7]: std::variant для вложенного класса
От: rg45 СССР  
Дата: 04.12.25 15:07
Оценка:
Здравствуйте, vopl, Вы писали:

R>>Вот если бы ещё получить ссылку на соответствующий пункт стандарта, было бы совсем здорово.


V>https://timsong-cpp.github.io/cppwp/class.mem.general#note-4


А как тогда объяснить это:

https://godbolt.org/z/oPo3o5ME7

struct D
{
    struct FService
    {
        int  nFService_ = 0;
    };
    static_assert(sizeof(FService) != 0);  // OK
    static_assert(std::default_initializable<FService>); // static assertion failed
};


Выходит, класс "достаточно полный" для sizeof, но недостаточно полный для того, чтобы понять, что он default infitializable. Это как так?
--
Справедливость выше закона. А человечность выше справедливости.
Re[8]: std::variant для вложенного класса
От: vopl Россия  
Дата: 04.12.25 15:19
Оценка:
Здравствуйте, rg45, Вы писали:

R>Здравствуйте, vopl, Вы писали:


R>>>Вот если бы ещё получить ссылку на соответствующий пункт стандарта, было бы совсем здорово.


V>>https://timsong-cpp.github.io/cppwp/class.mem.general#note-4


R>А как тогда объяснить это:


R>https://godbolt.org/z/oPo3o5ME7


R>
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. Это как так?
Re[9]: std::variant для вложенного класса
От: rg45 СССР  
Дата: 04.12.25 15:23
Оценка:
Здравствуйте, vopl, Вы писали:

R>>
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. В противном случае зачем эти сложности.
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 04.12.2025 15:30 rg45 . Предыдущая версия . Еще …
Отредактировано 04.12.2025 15:29 rg45 . Предыдущая версия .
Re[6]: std::variant для вложенного класса
От: sergii.p  
Дата: 04.12.25 15:48
Оценка: 16 (1) +1
Здравствуйте, 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.

Re[7]: std::variant для вложенного класса
От: Великий Мессия google
Дата: 04.12.25 15:55
Оценка: 24 (2)
Здравствуйте, 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
работают над исправлением как я понял,но вяло

https://github.com/llvm/llvm-project/issues/60321
Re[8]: std::variant для вложенного класса
От: rg45 СССР  
Дата: 05.12.25 10:28
Оценка: :)
Здравствуйте, Великий Мессия, Вы писали:

ВМ>о! нашел таки на llvm

ВМ>работают над исправлением как я понял,но вяло

ВМ>https://github.com/llvm/llvm-project/issues/60321


Ну вот, а мне тут про какие-то контексты лапшу на уши навешивают
--
Справедливость выше закона. А человечность выше справедливости.
Re[9]: std::variant для вложенного класса
От: sergii.p  
Дата: 05.12.25 10:51
Оценка: :))
Здравствуйте, rg45, Вы писали:

ВМ>>https://github.com/llvm/llvm-project/issues/60321


R>Ну вот, а мне тут про какие-то контексты лапшу на уши навешивают


ну так и в этом тикете такая же лапша. "Классы, контексты какие-то. Взять всё, и починить!". А меж тем тикету 3 года уже. Думаю разработчики сами плохо понимают аргументацию в стандарте, но это хорошая отмазка не чинить
Re[9]: std::variant для вложенного класса
От: B0FEE664  
Дата: 05.12.25 15:27
Оценка: 12 (1)
Здравствуйте, rg45, Вы писали:

R>Ну вот, а мне тут про какие-то контексты лапшу на уши навешивают


using value_type = float;

struct A
{
  struct B
  {
     value_type n = 0;
  };
  using value_type = int;
};
И каждый день — без права на ошибку...
Re[10]: std::variant для вложенного класса
От: sergii.p  
Дата: 05.12.25 16:03
Оценка:
Здравствуйте, 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;
};
Re[10]: std::variant для вложенного класса
От: rg45 СССР  
Дата: 05.12.25 17:05
Оценка:
Здравствуйте, B0FEE664, Вы писали:

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>


Не понял я что ты хотел сказать этим примером. По сути ты демонстрируешь следующее:

using value_type = float;

struct A
{
    static_assert(std::same_as<float, value_type>);
    using value_type = int;
    static_assert(std::same_as<int, value_type>);
};


Как это относится к обсуждаемой проблеме? Замени классы на пространства имен, получишь ровно то же самое.

А gcc на твой пример вообще плюнется ошибкой: "declaration of 'using A::value_type = int' changes meaning of 'value_type'".
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 05.12.2025 17:37 rg45 . Предыдущая версия . Еще …
Отредактировано 05.12.2025 17:28 rg45 . Предыдущая версия .
Отредактировано 05.12.2025 17:26 rg45 . Предыдущая версия .
Отредактировано 05.12.2025 17:24 rg45 . Предыдущая версия .
Отредактировано 05.12.2025 17:10 rg45 . Предыдущая версия .
Отредактировано 05.12.2025 17:07 rg45 . Предыдущая версия .
Re[11]: std::variant для вложенного класса
От: rg45 СССР  
Дата: 05.12.25 17:55
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>
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 используется литеральный ноль в качестве инициализатора, куда мы в этом случае ещё не добрались?
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 05.12.2025 18:19 rg45 . Предыдущая версия . Еще …
Отредактировано 05.12.2025 18:08 rg45 . Предыдущая версия .
Отредактировано 05.12.2025 18:01 rg45 . Предыдущая версия .
Отредактировано 05.12.2025 17:56 rg45 . Предыдущая версия .
Отредактировано 05.12.2025 17:55 rg45 . Предыдущая версия .
Re[11]: std::variant для вложенного класса
От: B0FEE664  
Дата: 05.12.25 18:17
Оценка:
Здравствуйте, 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 здесь

а clang компилирует без предупреждений и выводит 2.3

clang здесь


код можно немного усложнить (с тем же эффектом: 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;}
};


А здесь вообще волшебный результат:

constexpr float f(int)
{
  return 0;
}

struct A
{
  using value_Type = decltype(f(0));
  struct B
  {
     value_Type             n = f(0);
     static constexpr auto  m = f(0);
  } b;

   static constexpr  int f(int) { return 5;}
};

int main() 
{
  A a;
  std::cout << "n=" << a.b.n << '\n';
  std::cout << "m=" << a.b.m << '\n';

  return 0;
}


Program stdout
n=5
m=0
И каждый день — без права на ошибку...
Re: std::variant для вложенного класса
От: rg45 СССР  
Дата: 05.12.25 18:24
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Почему некоторые компиляторы это отказываются компилировать ?

BFE>
BFE>#include <variant>

BFE>struct D
BFE>{
BFE>    struct FService
BFE>    {
BFE>        int  nFService_ = 0;
BFE>    };

BFE>    std::variant<FService> data_;
BFE>};

BFE>int main() 
BFE>{
BFE>D d;
BFE>return 0;
BFE>}
BFE>


BFE>здесь


По-моему, в стандарте явно не хватает разделения правил для инициализаторов, зависящих и не зависящих от контекста обрамляющего класса. Типа как существуют различные правила для выражений зависящих и не зависящих от параметров шаблона. Ну, провтыкали они этот момент, бывает.
--
Справедливость выше закона. А человечность выше справедливости.
Re[11]: std::variant для вложенного класса
От: B0FEE664  
Дата: 05.12.25 18:41
Оценка: :)
Здравствуйте, rg45, Вы писали:

  Скрытый текст
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>>

R>Не понял я что ты хотел сказать этим примером.
Имя. Где искать имя?
Почему при декларации используется один порядок поиска имён, а при вызове — другой?
И каждый день — без права на ошибку...
Re[12]: std::variant для вложенного класса
От: rg45 СССР  
Дата: 05.12.25 18:44
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>То есть вы считаете, что поиск по имени константы и по имени типа является (и должен быть) различным? (как правильно я не разбирался)


Там с этим unqualified name lookup сам чёрт лапы поломает. Но по факту выходит что да, правила разные.
--
Справедливость выше закона. А человечность выше справедливости.
Re[12]: std::variant для вложенного класса
От: rg45 СССР  
Дата: 05.12.25 18:45
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Имя. Где искать имя?

BFE>Почему при декларации используется один порядок поиска имён, а при вызове — другой?

Да вот, тоже сижу, втыкаю, пытаюсь разобраться. По факту получается, что правила разные для типов и для констант (объектов). А вот почему так...

Наиболее читабельно расписано вот здесь: https://en.cppreference.com/w/cpp/language/unqualified_lookup.html. Но то, что тут написано не очень согласуется с наблюдаемым поведением.
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 05.12.2025 18:48 rg45 . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.