Приведение базового класса в конструкторе потомка
От: Евгений Музыченко Франция http://software.muzychenko.net/rus
Дата: 02.09.19 14:22
Оценка:
Компиляторы MS VC++ на такой вот код:

struct Base {
  int a;
  int b;
};

struct Derived : public Base {
  Derived (Base const B) { *this = B; }
};

Derived f (Base const B) {
  return B;
}


стабильно жалуются на то, что конструктор Derived получается рекурсивным за счет того, что для преобразования Base к Derived используется он же.

Это особенность компиляторов MS, или оно везде так?

Если бы данных в классе было много, можно было бы копировать через приведение указателей, но структуры до 64 разрядов укладываются в "интегральный тип", хорошо обрабатываются в константных выражениях, и без проблем передаются на регистрах.

Можно ли как-то заставить компилятор приводить типы совсем тупо? Ни reinterpret_cast, ни function-style cast, ни C-style cast не помогают.

Знаю, что можно включить Base в Derived вместо наследования, и наделать операторов преобразования, но хотелось бы именно с наследованием.
base derived constuctor type conversion cast базовый потомок тип приведение преобразование
Re: Приведение базового класса в конструкторе потомка
От: night beast СССР  
Дата: 02.09.19 14:34
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Компиляторы MS VC++ на такой вот код:


ЕМ>struct Derived : public Base {
ЕМ>  Derived (Base const B) { *this = B; }
ЕМ>};

ЕМ>Derived f (Base const B) {
ЕМ>  return B;
ЕМ>}


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


ссылки нигде не забыл?

давай может лучше напишешь с примерами, что именно хочешь получить в итоге, а то не очень понятно
Re: Приведение базового класса в конструкторе потомка
От: rg45 СССР  
Дата: 02.09.19 14:37
Оценка: 12 (1) +5
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>
ЕМ>struct Base {
ЕМ>  int a;
ЕМ>  int b;
ЕМ>};

ЕМ>struct Derived : public Base {
ЕМ>  Derived (Base const B) { *this = B; }
ЕМ>};

ЕМ>Derived f (Base const B) {
ЕМ>  return B;
ЕМ>}
ЕМ>


ЕМ>Можно ли как-то заставить компилятор приводить типы совсем тупо? Ни reinterpret_cast, ни function-style cast, ни C-style cast не помогают.


Про списки инициализации не слыхал?

struct Derived : public Base {
  Derived (Base const B) : Base(B) { }
};


P.S. Я уже не спрашиваю, почему параметр передается по значению, почему конструктор не explicit и зачем оно вообще нужно.
--
http://static.skaip.su/img/emoticons/v2/ffffff/headbang.gif
Re: Приведение базового класса в конструкторе потомка
От: kov_serg Россия  
Дата: 02.09.19 14:40
Оценка: +1
Здравствуйте, Евгений Музыченко, Вы писали:

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

А если написать так:
struct Derived : public Base {
 Derived(Base const B) : Base(B) { }
};
Re: Приведение базового класса в конструкторе потомка
От: cserg  
Дата: 02.09.19 14:45
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Можно ли как-то заставить компилятор приводить типы совсем тупо? Ни reinterpret_cast, ни function-style cast, ни C-style cast не помогают.

Вот такое разве не работает?
Derived(Base const B) { (*(Base*)this) = B; }
Re[2]: Приведение базового класса в конструкторе потомка
От: Евгений Музыченко Франция http://software.muzychenko.net/rus
Дата: 02.09.19 15:13
Оценка:
Здравствуйте, night beast, Вы писали:

NB>ссылки нигде не забыл?


Нет.

NB>давай может лучше напишешь с примерами, что именно хочешь получить в итоге, а то не очень понятно


Да этого примера достаточно. Нужно, чтобы все передавалось по значению, через регистры.
Re[2]: Приведение базового класса в конструкторе потомка
От: Евгений Музыченко Франция http://software.muzychenko.net/rus
Дата: 02.09.19 15:19
Оценка:
Здравствуйте, rg45, Вы писали:

R>Про списки инициализации не слыхал?


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

R>почему параметр передается по значению


Чтобы оптимизатор делал передачу структур до 64 разрядов через регистры. Впрочем, для не-POD этого, похоже, не добиться — он всегда генерит временную переменную, даже если по факту никаких вызовов конструкторов/деструкторов там нет (все вычисляется во время компиляции).

R>почему конструктор не explicit


Именно, чтобы можно было преобразовывать неявно.

R>и зачем оно вообще нужно.


В реальном коде 32-разрядная структура из битовых полей. Ее и удобнее, и выгоднее держать на регистре.
Re[2]: Приведение базового класса в конструкторе потомка
От: Евгений Музыченко Франция http://software.muzychenko.net/rus
Дата: 02.09.19 15:20
Оценка:
Здравствуйте, cserg, Вы писали:

C>Вот такое разве не работает?

C>
C>Derived(Base const B) { (*(Base*)this) = B; }
C>


Работает, как и взятие укаателя на B и приведение его к указателю на Derived. Но при этом все заведомо будет в памяти.
Re[3]: Приведение базового класса в конструкторе потомка
От: Zhendos  
Дата: 02.09.19 17:06
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

R>>почему параметр передается по значению


ЕМ>Чтобы оптимизатор делал передачу структур до 64 разрядов через регистры. Впрочем, для не-POD этого, похоже, не добиться — он всегда генерит временную переменную, даже если по факту никаких вызовов конструкторов/деструкторов там нет (все вычисляется во время компиляции).


можно написать так:

struct Derived : public Base {
 Derived(Base B) : Base(std::move(B)) { }
};
Re[3]: Приведение базового класса в конструкторе потомка
От: cserg  
Дата: 02.09.19 17:15
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Работает, как и взятие укаателя на B и приведение его к указателю на Derived.

Только нельзя так делать.

ЕМ>Но при этом все заведомо будет в памяти.

Вы ошибаетесь. Операция взятия адреса еще не означает, что адресуемый объект обязательно будет размещен в памяти.
Re[4]: Приведение базового класса в конструкторе потомка
От: Евгений Музыченко Франция http://software.muzychenko.net/rus
Дата: 02.09.19 18:27
Оценка:
Здравствуйте, cserg, Вы писали:

ЕМ>>Работает, как и взятие укаателя на B и приведение его к указателю на Derived.


C>Только нельзя так делать.


С чего бы вдруг? Совершенно типовая операция.

C>Операция взятия адреса еще не означает, что адресуемый объект обязательно будет размещен в памяти.


Теоретически. А практически это умеют только самые зверские оптимизаторы (у MS такого нет).
Re[2]: Приведение базового класса в конструкторе потомка
От: Zhendos  
Дата: 02.09.19 18:39
Оценка: +1 -1
Здравствуйте, cserg, Вы писали:

C>Здравствуйте, Евгений Музыченко, Вы писали:


ЕМ>>Можно ли как-то заставить компилятор приводить типы совсем тупо? Ни reinterpret_cast, ни function-style cast, ни C-style cast не помогают.

C>Вот такое разве не работает?
C>
C>Derived(Base const B) { (*(Base*)this) = B; }
C>


по-моему что-нибудь типа
Derived(Base const B) { this->Base::operator=(B); }


будет намного более понятнее чем приведение указателей
Re[3]: Приведение базового класса в конструкторе потомка
От: Евгений Музыченко Франция http://software.muzychenko.net/rus
Дата: 02.09.19 18:58
Оценка:
Здравствуйте, Zhendos, Вы писали:

Z> будет намного более понятнее чем приведение указателей


Задача не в понятности, а в избавлении от излишних пересылок.
Re[4]: Приведение базового класса в конструкторе потомка
От: andyp  
Дата: 02.09.19 20:03
Оценка:
Здравствуйте, Zhendos, Вы писали:



Z>можно написать так:


Z>
Z>struct Derived : public Base {
Z> Derived(Base B) : Base(std::move(B)) { }
Z>};
Z>


Имхо B тогда протухает и уже недоступно в теле конструктора, могут быть связанные с этим залеты. А так, для условий ТС (B — куча бит без указателей внутри) особого выигрыша по производительности не будет имхо.
Re[2]: Приведение базового класса в конструкторе потомка
От: Hobbes Россия  
Дата: 02.09.19 20:09
Оценка: +1
Здравствуйте, rg45, Вы писали:

R>P.S. Я уже не спрашиваю, почему параметр передается по значению, почему конструктор не explicit и зачем оно вообще нужно.


Передача параметра конструктора по значению в сочетании с std::move — стильно, модно, молодёжно. Но тут Евгений чертовщину какую-то делает. Особенно, если он пытается сделать инициализацию базы.
Re[3]: Приведение базового класса в конструкторе потомка
От: rg45 СССР  
Дата: 03.09.19 05:17
Оценка: +1
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Чтобы оптимизатор делал передачу структур до 64 разрядов через регистры. Впрочем, для не-POD этого, похоже, не добиться — он всегда генерит временную переменную, даже если по факту никаких вызовов конструкторов/деструкторов там нет (все вычисляется во время компиляции).


R>>почему конструктор не explicit

ЕМ>Именно, чтобы можно было преобразовывать неявно.

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

C++ Coding Standards. 101 Rules, Guidelines, and Best Practices.
(Цитаты взяты из русского перевода книги).

Правило №8. Не оптимизируйте преждевременно

Передача параметров по ссылке (рекомендация 25), использование префиксной формы операторов ++ и -- (рекомендация 28) или подобных идиом, которые при работе должны естественным образом "стекать с кончиков ваших пальцев", преждевременной оптимизацией не являются. Это всего лишь устранение преждевременной пессимизации (рекомендация 9).


Правило №9. Не пессимизируйте преждевременно

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


Правило №15. Активно используйте const

Пример. Избегайте const в объявлениях функций, принимающих параметры по значению. Два следующих объявления абсолютно эквивалентны:

void Fun( int x );
void Fun( const int x );  // Объявление той же самой функции:
                          // const здесь игнорируется

Во втором объявлении модификатор const избыточен. Мы рекомендуем объявлять функции без таких высокоуровневых модификаторов const, чтобы тот, кто читает ваши заголовочные файлы, не был дезориентирован...


Правило №40. Избегайте возможностей неявного преобразования типов

Не все изменения прогрессивны: неявные преобразования зачастую приносят больше вреда, чем пользы. Дважды подумайте перед тем, как предоставить возможность неявного преобразования к типу и из типа, который вы определяете, и предпочитайте полагаться на явные преобразования (используйте конструкторы, объявленные как explicit, и именованные функции преобразования типов).

--
http://static.skaip.su/img/emoticons/v2/ffffff/headbang.gif
Отредактировано 03.09.2019 8:04 rg45 . Предыдущая версия . Еще …
Отредактировано 03.09.2019 8:03 rg45 . Предыдущая версия .
Отредактировано 03.09.2019 5:56 rg45 . Предыдущая версия .
Отредактировано 03.09.2019 5:54 rg45 . Предыдущая версия .
Отредактировано 03.09.2019 5:43 rg45 . Предыдущая версия .
Отредактировано 03.09.2019 5:29 rg45 . Предыдущая версия .
Отредактировано 03.09.2019 5:25 rg45 . Предыдущая версия .
Отредактировано 03.09.2019 5:20 rg45 . Предыдущая версия .
Re[3]: Приведение базового класса в конструкторе потомка
От: Евгений Музыченко Франция http://software.muzychenko.net/rus
Дата: 03.09.19 10:16
Оценка:
Здравствуйте, Hobbes, Вы писали:

H>тут Евгений чертовщину какую-то делает. Особенно, если он пытается сделать инициализацию базы.


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

А чертовщина — это то, что в MS VC++, например, обычный метод класса, возвращающий по значению POD-структуру размером до восьми байт, делает это через регистры. Но стоит сделать этот метод виртуальным, как он возвращает то же самое исключительно через временную переменную на стеке. Ладно бы возвращаемый элемент сам был классом, но это банальная POD-структура, которая от класса, в котором определяется метод, никак не зависит. Я в полных непонятках о причине такой особенности.
Re[4]: Приведение базового класса в конструкторе потомка
От: Евгений Музыченко Франция http://software.muzychenko.net/rus
Дата: 03.09.19 10:19
Оценка:
Здравствуйте, rg45, Вы писали:

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


Я ее как раз недавно перечитывал. Охотно последую этим советам, если кто-нибудь сумеет внятно объяснить, почему 32-разрядный int "безусловно следует" передавать по значению, а 32-разрядную структуру — столь же "безусловно", но по ссылке.
Re[5]: Приведение базового класса в конструкторе потомка
От: rg45 СССР  
Дата: 03.09.19 11:50
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

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


ЕМ>Я ее как раз недавно перечитывал. Охотно последую этим советам, если кто-нибудь сумеет внятно объяснить, почему 32-разрядный int "безусловно следует" передавать по значению, а 32-разрядную структуру — столь же "безусловно", но по ссылке.


Ну насчет "безусловно" это ты загнул, конечно. Ну ОК, объекты небольших структур с недорогим копированием, типа твоего Base, вполне допустимо передавать по значению. Об этом говорится в рекомендации №25. Однако способом передачи по умолчанию все же является передача по константной ссылке. Генеруемые компилятором конструкторы копирования и копирующие операторы присванивания имеют именно такой вид. Все стандартные шаблонные функции, наподобие push_back, например, принимают параметры по константной ссылке и никого не парит возможная потеря производительности при работе с целыми числами. Ну какой смысл в этом промежуточном копировании в конструкторе Derived, если в итоге копирование пойдет через конструктор копирования Base, который все равно будет работать по константной ссылке на эту промежуточную копию Не лучше ли просто пробросить ему ссылку на оригинал?

Ну а что насчет других рекомендаций — касающихся неявных преобразований и модификаторов const при параметрах? Тоже не согласен с чем-то?
--
http://static.skaip.su/img/emoticons/v2/ffffff/headbang.gif
Отредактировано 03.09.2019 12:37 rg45 . Предыдущая версия . Еще …
Отредактировано 03.09.2019 12:35 rg45 . Предыдущая версия .
Отредактировано 03.09.2019 12:34 rg45 . Предыдущая версия .
Отредактировано 03.09.2019 12:30 rg45 . Предыдущая версия .
Отредактировано 03.09.2019 12:14 rg45 . Предыдущая версия .
Отредактировано 03.09.2019 12:07 rg45 . Предыдущая версия .
Re[6]: Приведение базового класса в конструкторе потомка
От: Евгений Музыченко Франция http://software.muzychenko.net/rus
Дата: 03.09.19 13:20
Оценка:
Здравствуйте, rg45, Вы писали:

R>Ну а что насчет других рекомендаций — касающихся неявных преобразований и модификаторов const при параметрах? Тоже не согласен с чем-то?


Разумеется. За использование неявных преобразований имеет смысл пенять только после того, как в языке сделают мало-мальски управляемую систему integral promotions, которая во многих случаях совершенно бессмысленна, и при попытке скрестить ее с шаблонами/перегрузками заставляет городить чрезмерно сложные конструкции. Я на это здесь уже ругался раз
Автор: Евгений Музыченко
Дата: 10.10.17
и два
Автор: Евгений Музыченко
Дата: 09.10.18
.

Модификатор const у параметра-значения в объявлении функции действительно не имеет смысла, но объявление ж не существует само по себе — или оно копируется из заголовка определения, или заголовок определения копируется из него. Если в определениях используется const — какой смысл старательно вычищать его из объявлений, или добавлять в определения после копирования?
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.