? Зачем виртуальный метод запрещает структурную инициализаци
От: Sm0ke Россия ksi
Дата: 22.08.23 17:36
Оценка: +1
Вот есть простые структуры. Без явных конструкторов.
Как же удобно для них создавать объекты с почленной инициализацией.
Хочешь все укажи, хочешь некоторые.
С 20-ым стандартом можно по имени свойства значения задавать. (aka designated)

#include <iostream>

using t_diff = std::ptrdiff_t;

struct t_param
{
  // data
  t_diff
    count{};
  bool
    flag{};

  //virtual void no() const {}
};

int main()
{
  t_param o{1};
  t_param p{.flag = true}; // designated / by prop name
  return 0;
}


Но стоит только туда добавить хоть один виртуальный метод ...
... И всё! Для инициализации полей пиши конструктор


Зачем они так?
Даже не-виртуальный деструктор картины не портит.
Что не так с виртуальными методами? Не вижу причины запрещать прежнюю инициализацию с ними!

#include <iostream>

using t_diff = std::ptrdiff_t;

struct t_param
{
  // data
  t_diff
    count{};
  bool
    flag{};

  virtual void no() const {}
};

int main()
{
  t_param o{1};
  t_param p{.flag = true};
  return 0;
}


x86-64 clang 13.0.0 (Editor #1)

x86-64 clang 13.0.0
x86-64 clang 13.0.0
see detailed flags window
123
<Compilation failed>

# For more information see the output window
x86-64 clang 13.0.0 — cached
Output of x86-64 clang 13.0.0 (Compiler #1)
<source>:18:11: error: no matching constructor for initialization of 't_param'
t_param o{1};
^~~~
<source>:5:8: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'int' to 'const t_param' for 1st argument
struct t_param
^
<source>:5:8: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'int' to 't_param' for 1st argument
struct t_param
^
<source>:5:8: note: candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided
<source>:19:11: error: no matching constructor for initialization of 't_param'
t_param p{.flag = true};
^~~~~~~~~~~~~~~
<source>:5:8: note: candidate constructor (the implicit copy constructor) not viable: cannot convert argument of incomplete type 'void' to 'const t_param' for 1st argument
struct t_param
^
<source>:5:8: note: candidate constructor (the implicit move constructor) not viable: cannot convert argument of incomplete type 'void' to 't_param' for 1st argument
struct t_param
^
<source>:5:8: note: candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided
2 errors generated.
Compiler returned: 1

Отредактировано 22.08.2023 18:32 Sm0ke . Предыдущая версия . Еще …
Отредактировано 22.08.2023 17:40 Sm0ke . Предыдущая версия .
Отредактировано 22.08.2023 17:37 Sm0ke . Предыдущая версия .
Re: ? Зачем виртуальный метод запрещает структурную инициали
От: pokutan Израиль http://pokutan.com/
Дата: 22.08.23 18:29
Оценка:
думаю, вопрос немного в другой плоскости

читаем aggregate_initialization

An aggregate is one of the following types:

array type
class type (typically, struct or union), that has
.........
.........
no virtual member functions

тип агрегатор имеет кучу ограничений, среди прочего — не может иметь виртуальных методов
Отредактировано 22.08.2023 18:32 pokutan . Предыдущая версия . Еще …
Отредактировано 22.08.2023 18:31 pokutan . Предыдущая версия .
Re[2]: ? Зачем виртуальный метод запрещает структурную иници
От: Sm0ke Россия ksi
Дата: 22.08.23 18:34
Оценка:
Здравствуйте, pokutan, Вы писали:

P>думаю, вопрос немного в другой плоскости


P>читаем aggregate_initialization


P>An aggregate is one of the following types:


P>array type

P>class type (typically, struct or union), that has
P>.........
P>.........
P>no virtual member functions

P>тип агрегатор имеет кучу ограничений, среди прочего — не может иметь виртуальных методов


я про это и написал.
Но в чём причина добавлять такое ограничение в стандарт? Без него было бы лучше.
Отредактировано 22.08.2023 18:35 Sm0ke . Предыдущая версия .
Re[3]: ? Зачем виртуальный метод запрещает структурную иници
От: rg45 СССР  
Дата: 22.08.23 18:37
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>я про это и написал.

S>Но в чём причина добавлять такое ограничение в стандарт? Без него было бы лучше.

А х его з. Список ограничений агрегатных типов здорово коррелирует с ограничениями Standard-layout class, вероятно, это как-то взаимосвязано.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[3]: ? Зачем виртуальный метод запрещает структурную иници
От: pokutan Израиль http://pokutan.com/
Дата: 22.08.23 18:41
Оценка:
Здравствуйте, Sm0ke, Вы писали:

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


P>>думаю, вопрос немного в другой плоскости


P>>читаем aggregate_initialization


P>>An aggregate is one of the following types:


P>>array type

P>>class type (typically, struct or union), that has
P>>.........
P>>.........
P>>no virtual member functions

P>>тип агрегатор имеет кучу ограничений, среди прочего — не может иметь виртуальных методов


S>я про это и написал.

S>Но в чём причина добавлять такое ограничение в стандарт? Без него было бы лучше.

ну, наверное, это логично с точки зрения семантики, агрегатор — просто-напросто контейнер для разных данных, он не предполагает делегирования (ака переопределения в наследниках) операций для манипулирования этими данными
Re[4]: ? Зачем виртуальный метод запрещает структурную иници
От: Sm0ke Россия ksi
Дата: 22.08.23 21:19
Оценка:
Здравствуйте, rg45, Вы писали:

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


S>>Но в чём причина добавлять такое ограничение в стандарт? Без него было бы лучше.


R>А х его з. Список ограничений агрегатных типов здорово коррелирует с ограничениями Standard-layout class, вероятно, это как-то взаимосвязано.


Думаю для компилятора это всё не проблема. Если он знает как параметры конструктора передать в члены, то почему бы не позволить инициализировать их напрямую?
Возможно в конструкторах класса с vtable есть какой-то дополнительный исполняемый код, но его-же можно добавить и при структурной инициализации ...
Компилятор же может инлайнить конструкторы классов и с vtable и без.
Re[4]: ? Зачем виртуальный метод запрещает структурную иници
От: Sm0ke Россия ksi
Дата: 22.08.23 21:31
Оценка:
Здравствуйте, pokutan, Вы писали:

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


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


P>>>думаю, вопрос немного в другой плоскости


P>>>читаем aggregate_initialization


P>>>An aggregate is one of the following types:


P>>>array type

P>>>class type (typically, struct or union), that has
P>>>.........
P>>>.........
P>>>no virtual member functions

P>>>тип агрегатор имеет кучу ограничений, среди прочего — не может иметь виртуальных методов


S>>я про это и написал.

S>>Но в чём причина добавлять такое ограничение в стандарт? Без него было бы лучше.

P>ну, наверное, это логично с точки зрения семантики, агрегатор — просто-напросто контейнер для разных данных, он не предполагает делегирования (ака переопределения в наследниках) операций для манипулирования этими данными


Скорее всего я не понял что вы написали

Но вот так же можно:
struct t_param
{
  bool flag;
  int count;
};

class t_object : public t_param
{
  t_object() : t_param{false, 1} {}
  virtual void ok() {}
};


? Так в чём проблема позволить и Это:
struct t_param
{
  bool flag;
  int count;

  virtual void ok() {}
};

class t_object : public t_param
{
public:
  t_object() : t_param{false, 1} {} // зачем-то ошибка компиляции
  void ok() override {}
};
Отредактировано 22.08.2023 21:33 Sm0ke . Предыдущая версия .
Re[5]: ? Зачем виртуальный метод запрещает структурную иници
От: vdimas Россия  
Дата: 25.08.23 08:57
Оценка: 2 (1)
Здравствуйте, Sm0ke, Вы писали:

S>Возможно в конструкторах класса с vtable есть какой-то дополнительный исполняемый код


Ну да, первым полем в любом конструкторе неявно инициализируется указатель на vtable.

При вызове всех конструкторов по цепочке от базы к наследникам это поле многократно перезаписывается (с точностью до оптимизации, если компилятор может инлайнить конструкторы — это почему конструкторы базовых классов лучше делать инлайными, дабы попытаться исключить многократную перезапись указателя на vtable в большинстве случаев).


S>но его-же можно добавить и при структурной инициализации ...


Ситуация наоборот — у объекта с виртуальными методами должен быть конструктор (хотя бы, по-умолчанию), иначе объект этого типа невозможно создать.
А если есть конструктор, то невозможна структурная инициализация.

Если в этом коде раскомментировать виртуальный метод, то как создать Derived? ))
struct Base {
    int a;
    int b;

    //Base(int a, int b) : a(a), b(b) {}

    //virtual void f() {}
};

struct Derived : Base {
    int c;
    
    //Derived(const Base & b, int c)
    //   : Base(b), c(c) {}
};

int main() {
    Derived obj { {42, 43}, 44 };
    std::cout << obj.a << ", " << obj.b << ", " << obj.c << std::endl;
}

(пусть даже не через структурную инициализацию, а хоть как-нибудь?)

А если определить конструктор — прощай структурная инициализация.
Благо, в современном единообразном синтаксисе вызова конструкторов и структурной инициализации можно добавлять конструкторы, не меняя код в местах инициализации объектов (раскомментировать всё в сниппете, можно поочерёдно сверху, наблюдая совместную жизнь в одном синтаксисе вызовов конструкторов и структурной инициализации).
Отредактировано 25.08.2023 10:23 vdimas . Предыдущая версия . Еще …
Отредактировано 25.08.2023 10:21 vdimas . Предыдущая версия .
Отредактировано 25.08.2023 10:18 vdimas . Предыдущая версия .
Re[5]: ? Зачем виртуальный метод запрещает структурную иници
От: vdimas Россия  
Дата: 25.08.23 09:00
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S> t_object() : t_param{false, 1} {} // зачем-то ошибка компиляции


У t_param появился обязательный конструктор (по-умолчанию), соотв. t_param{false, 1} пытается найти один из имеющихся конструкторов (согласно сигнатуре) и не находит.
Re: ? Зачем виртуальный метод запрещает структурную инициализаци
От: Pzz Россия https://github.com/alexpevzner
Дата: 25.08.23 12:44
Оценка: :)
Здравствуйте, Sm0ke, Вы писали:

S>Зачем они так?

S>Даже не-виртуальный деструктор картины не портит.
S>Что не так с виртуальными методами? Не вижу причины запрещать прежнюю инициализацию с ними!

Я бы предположил, что не всякий линкер и не на всякой системе способен статически сконструировать объект с виртуальными методами. Поэтому для совместимости они и ввели такое ограничение.
Re[2]: ? Зачем виртуальный метод запрещает структурную инициализаци
От: koenjihyakkei Россия  
Дата: 25.08.23 13:08
Оценка:
Здравствуйте, Pzz, Вы писали:

Pzz>Я бы предположил, что не всякий линкер и не на всякой системе способен статически сконструировать объект с виртуальными методами. Поэтому для совместимости они и ввели такое ограничение.


А причем здесь линкер и виртуальная таблица? Мне кажется, в данном случае проблему может составить только указатель на виртуальную таблицу, который хранится в объекте класса. Причем как он хранится, не стандартизовано. Может, из-за этого и ввели такое ограничение... Например, если vpointer хранится как-то супер эзотерически, настолько, что становится невозможно разложить значения по полям класса при его наличии.
Re[6]: ? Зачем виртуальный метод запрещает структурную иници
От: Sm0ke Россия ksi
Дата: 25.08.23 13:59
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Ситуация наоборот — у объекта с виртуальными методами должен быть конструктор (хотя бы, по-умолчанию), иначе объект этого типа невозможно создать.


Возможно создать

~ https://godbolt.org/z/9d9q9nne3
#include <iostream>

using t_diff = std::ptrdiff_t;

struct t_param
{
  // data
  t_diff
    count{};
  bool
    flag{};

  virtual void go() const {}
};

int main()
{
  t_param o{};
  return 0;
}


Вы можете ответить мол конструктор по умолчанию сгенерирован компилятором (и заинлайнен)
И что? Без вирт метода тоже можно обращаться к конструктору по умолчанию в агрегатной структуре.

#include <iostream>

using t_diff = std::ptrdiff_t;

struct t_param
{
  // data
  t_diff
    count{};
  bool
    flag{};
};

int main()
{
  t_param o{};
  return 0;
}
Re[7]: ? Зачем виртуальный метод запрещает структурную иници
От: vdimas Россия  
Дата: 25.08.23 16:22
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>Вы можете ответить мол конструктор по умолчанию сгенерирован компилятором


Верно.


S>И что? Без вирт метода тоже можно обращаться к конструктору по умолчанию в агрегатной структуре.


В случае виртуальных методов необходимо ссылаться на единственный vtable в автогенерённых в каждом модуле конструкторах, иначе dynamic_cast может перестать работать.

Наверное, при экспорте такой структуры необходимо так же материализовать и экспортировать символ автосгенерённого дефолтного конструктора.
(или экспортировать его vtable)

Ты хочешь добавить то допущение, что для случая структурной инициализации классов/структур с виртуальныи методами объект предварительно инициализируется автосгенерённым конструктором по-умолчанию (если он доступен, иначе структурная инициализация невозможна, ес-но)?

Далее рассуждения вслух (могу ошибаться).

Если тип импортируется, тогда поля могут быть проиницилизированы дважды — в умолчательном конструкторе и в целевой структурной инициализации.

Однако, инициализируемые поля могут представлять из себя нетривиальные типы и/или содержать новомодную дефолотную инициализацию. При перезаписи таких полей должны будут уже вызываться не конструкторы, а operator=, доступность которого может не совпадать с доступностью конструкторов.

Т.е., вместо operator= надо "честно" повторно инициализировать нетривиальное поле через парный вызов деструктора/конструктора — уже выглядит как антипаттерн. ))
В случае побочных эффектов в конструкторах/деструкторах получим потенциальное нарушение семантики программы.

=====================
В альтернативных рассуждениях пусть экспортируется vtable целевого типа, тогда код инициализации без проблем генерится на лету, как для структур без виртуальных методов.

Вполне может быть, что такие обсуждения есть — надо поискать по обсуждениям стандарта.
Отредактировано 25.08.2023 16:24 vdimas . Предыдущая версия .
Re[8]: ? Зачем виртуальный метод запрещает структурную иници
От: Sm0ke Россия ksi
Дата: 25.08.23 18:49
Оценка:
Здравствуйте, vdimas, Вы писали:

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


S>>Вы можете ответить мол конструктор по умолчанию сгенерирован компилятором


V>Верно.



S>>И что? Без вирт метода тоже можно обращаться к конструктору по умолчанию в агрегатной структуре.


V>В случае виртуальных методов необходимо ссылаться на единственный vtable в автогенерённых в каждом модуле конструкторах, иначе dynamic_cast может перестать работать.


Это же задача линкера, в любом случае. Раз он справляется с этим при наличии конструкторов — думаю не трудно сделать чтобы это было так и без.

V>Наверное, при экспорте такой структуры необходимо так же материализовать и экспортировать символ автосгенерённого дефолтного конструктора.

V>(или экспортировать его vtable)

V>Ты хочешь добавить то допущение, что для случая структурной инициализации классов/структур с виртуальныи методами объект предварительно инициализируется автосгенерённым конструктором по-умолчанию (если он доступен, иначе структурная инициализация невозможна, ес-но)?


V>Далее рассуждения вслух (могу ошибаться).


V>Если тип импортируется, тогда поля могут быть проиницилизированы дважды — в умолчательном конструкторе и в целевой структурной инициализации.


V>Однако, инициализируемые поля могут представлять из себя нетривиальные типы и/или содержать новомодную дефолотную инициализацию. При перезаписи таких полей должны будут уже вызываться не конструкторы, а operator=, доступность которого может не совпадать с доступностью конструкторов.


V>Т.е., вместо operator= надо "честно" повторно инициализировать нетривиальное поле через парный вызов деструктора/конструктора — уже выглядит как антипаттерн. ))

V>В случае побочных эффектов в конструкторах/деструкторах получим потенциальное нарушение семантики программы.

Компилятор может сгенерировать скрытый метод для инициализации только vtable, чтобы вызывать его (или инлайнить) при структурной инициализации. Когда нет явных конструкторов, конечно.
Отредактировано 25.08.2023 18:52 Sm0ke . Предыдущая версия .
Re: ? Зачем виртуальный метод запрещает структурную инициализаци
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 26.08.23 14:47
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>Но стоит только туда добавить хоть один виртуальный метод ...

S>... И всё! Для инициализации полей пиши конструктор


Я это ограничение обхожу, добавляя производный от структуры класс (или включая ее туда), в котором делаю конструкторы и операторы преобразования туда/сюда. Не слишком красиво, зато очень удобно для разных данных табличного характера.
Re[2]: ? Зачем виртуальный метод запрещает структурную инициализаци
От: Sm0ke Россия ksi
Дата: 26.08.23 19:01
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Здравствуйте, Sm0ke, Вы писали:


S>>Но стоит только туда добавить хоть один виртуальный метод ...

S>>... И всё! Для инициализации полей пиши конструктор


ЕМ>Я это ограничение обхожу, добавляя производный от структуры класс (или включая ее туда),


Мне это знакомо, тоже так делал

ЕМ> в котором делаю конструкторы и операторы преобразования туда/сюда. Не слишком красиво, зато очень удобно для разных данных табличного характера.


Делал "срез" через метод base() для свапа лишь определённого набора свойств всей структуры.

std::ranges::swap(this->base(), p_other.base()); // примерно так, а base() по ссылке возвращает
Re[2]: ? Зачем виртуальный метод запрещает структурную инициализаци
От: Sm0ke Россия ksi
Дата: 26.08.23 19:06
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Здравствуйте, Sm0ke, Вы писали:


S>>Но стоит только туда добавить хоть один виртуальный метод ...

S>>... И всё! Для инициализации полей пиши конструктор


ЕМ>Я это ограничение обхожу, добавляя производный от структуры класс (или включая ее туда), в котором делаю конструкторы и операторы преобразования туда/сюда. Не слишком красиво, зато очень удобно для разных данных табличного характера.


Однако это всё равно обходные пути.
Всё же согласитесь — было бы удобнее при vtable иметь возможность обходится без явных конструкторов класса для агрегатной инициализации.
Re[3]: ? Зачем виртуальный метод запрещает структурную инициализаци
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 26.08.23 20:11
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>было бы удобнее при vtable иметь возможность обходится без явных конструкторов


Многие неудобства C++ проистекают от фанатичного стремления к "абсолютной безопасности". Вместо жестких ограничений стоило бы ввести соответствующие атрибуты, использование которых вызывало бы предупреждения, которые приходилось бы явно подавлять из командной строки. В таком случае достаточно было бы посмотреть командную строку, чтобы увидеть, какие допущения эксплуатирует программа, и запустить компиляцию без подавляющих ключей, чтобы увидеть все потенциально опасные места.

Собственно, это было бы полезно для любых случаев UB. Но в Комитете, как известно, преобладают сторонники формализма.
Re[4]: ? конкретно в общих чертах
От: Sm0ke Россия ksi
Дата: 26.08.23 22:09
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Здравствуйте, Sm0ke, Вы писали:


S>>было бы удобнее при vtable иметь возможность обходится без явных конструкторов


ЕМ>Многие неудобства C++ проистекают от фанатичного стремления к "абсолютной безопасности". Вместо жестких ограничений стоило бы ввести соответствующие атрибуты, использование которых вызывало бы предупреждения, которые приходилось бы явно подавлять из командной строки. В таком случае достаточно было бы посмотреть командную строку, чтобы увидеть, какие допущения эксплуатирует программа, и запустить компиляцию без подавляющих ключей, чтобы увидеть все потенциально опасные места.


ЕМ>Собственно, это было бы полезно для любых случаев UB. Но в Комитете, как известно, преобладают сторонники формализма.


Вы не задумывались начать составление перечня конкретных таких нюансов языка си++ ? (возможно с указанием недостающих флагов командной строки, атрибутов, и предупреждений)
Отредактировано 26.08.2023 22:11 Sm0ke . Предыдущая версия . Еще …
Отредактировано 26.08.2023 22:09 Sm0ke . Предыдущая версия .
Re[5]: ? конкретно в общих чертах
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 27.08.23 13:31
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>не задумывались начать составление перечня конкретных таких нюансов языка си++ ? (возможно с указанием недостающих флагов командной строки, атрибутов, и предупреждений)


Задумываться начал много лет назад, но все время казалось, что это очевидные идеи, и их вот-вот реализуют. А теперь шанс на их реализацию еще меньше, ибо возобладал подход "безопасность должна обеспечиваться формальными методами, а не фактическими". То есть, если что-то можно написать небезопасно, то предпочитают это безусловно запрещать, а не предоставлять возможность пометить и изолировать. Сомневаюсь, что в таких условиях идея пометки/изоляции сможет сколько-нибудь заинтересовать разработчиков. По уму, надо было активно радеть за это хотя бы лет двадцать назад.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.