Оператор приведения
От: Videoman Россия http://www.htsproduction.com/
Дата: 04.07.19 10:30
Оценка:
Есть некий класс, условно Container, который хранит в себе разные другие типы, наподобие variant.
Что хотелось бы получить синтаксически:
const Container cont1 = double()
const Container cont2 = int();
const Container cont3 = string();
const Container cont4 = SomeClass();
// и т.д.

const double v1 = cont1;
const int v2 = cont2;
const string v3 = cont3;
const SomeClass v4 = cont4;
// и т.д.

//...

Казалось бы, задачу можно решить определив оператор приведения типа у класс Container, но вышеописанные выражения являются вызовами конструкторов инициализации, а следовательно у классов со сложными конструкторами (типа string) сначала срабатывают перегрузки конструкторов, а не операторов приведения типа. Следовательно нужная схема не работает. Как-нибудь просто можно решить задачу в рамках современного С++ ?
http://www.gravatar.com/avatar/60560936caa07b944d4c3cecf1c06cc5?s=80&d=identicon
Отредактировано 04.07.2019 10:31 Videoman . Предыдущая версия .
Re: Оператор приведения
От: rg45 СССР  
Дата: 04.07.19 10:39
Оценка:
Здравствуйте, Videoman, Вы писали:

V>Казалось бы, задачу можно решить определив оператор приведения типа у класс Container, но вышеописанные выражения являются вызовами конструкторов инициализации, а следовательно у классов со сложными конструкторами (типа string) сначала срабатывают перегрузки конструкторов, а не операторов приведения типа. Следовательно нужная схема не работает. Как-нибудь просто можно решить задачу в рамках современного С++ ?


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

Вот: https://ideone.com/xRSCq8
--
Самая страшная мафия это дураки — у них везде свои люди.
Отредактировано 04.07.2019 14:06 rg45 . Предыдущая версия .
Re[2]: Оператор приведения
От: Videoman Россия http://www.htsproduction.com/
Дата: 05.07.19 09:23
Оценка:
Здравствуйте, rg45, Вы писали:

Вот, постарался как можно более компактно изобразить проблему.

Строчка 489 — не правильно разрешается перегрузка в случае std::optional, пока не могу понять почему. Буду очень признателен если вы посоветуете как наиболее минимальными изменениями можно это пофиксить.
Не спрашивайте почему используется собственная реализация std::optional, но хотелось бы, по возможности, сейчас использовать именно ее .

Самом деле проблема глубже, на мой взгляд. Любой класс внутри контейнера который принимает один шаблонный параметр без explicit, будет пытаться вызывать конструктор инициализации вместо оператора приведения типа контейнера. Если я ошибаюсь поправьте меня.

P.S. Я в курсе что моя реализация optional сильно урезана и во многом не соответствует стандарту, но от нее много и не требуется. По ряду причин, я не могу использовать сторонние библиотеки (boost и т.д.) и пока-что, на данной итерации, мы все еще используем VS2013, где нет optional. Со стандартным optional из поставки VS2017 ваш пример работает. В будущем после переходе на C++17 все заработает, но нужно что-то предпринять сейчас.
http://www.gravatar.com/avatar/60560936caa07b944d4c3cecf1c06cc5?s=80&d=identicon
Отредактировано 05.07.2019 9:38 Videoman . Предыдущая версия .
Re[3]: Оператор приведения
От: rg45 СССР  
Дата: 05.07.19 09:41
Оценка:
Здравствуйте, Videoman, Вы писали:

V>Вот, постарался как можно более компактно изобразить проблему.


V>Строчка 489 — не правильно разрешается перегрузка в случае std::optional, пока не могу понять почему. Буду очень признателен если вы посоветуете как наиболее минимальными изменениями можно это пофиксить.


Ну это понятно почему — в цепочке присутствуют два последовательных пользовательских преобразования — оператор приведения Container и преобразующий конструктор optional, тогда как стандарт позволяет только одно. Тут нужна либо специализированная конвертирующая функция, либо явное преобразование к int:

const std::optional<int> v5(int(cont5));
--
Самая страшная мафия это дураки — у них везде свои люди.
Re[3]: Оператор приведения
От: night beast СССР  
Дата: 05.07.19 09:46
Оценка: 2 (1)
Здравствуйте, Videoman, Вы писали:

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


V>Вот, постарался как можно более компактно изобразить проблему.


V>Строчка 489 — не правильно разрешается перегрузка в случае std::optional, пока не могу понять почему. Буду очень признателен если вы посоветуете как наиболее минимальными изменениями можно это пофиксить.

V>Не спрашивайте почему используется собственная реализация std::optional, но хотелось бы, по возможности, сейчас использовать именно ее .


замени
        template <typename OtherType = Type>
        optional(compatible_t<OtherType>&& value) noexcept;

на
        template <typename OtherType = Type>
        optional(OtherType&& value, std::decay_t<compatible_t<OtherType>>* ptr = 0) noexcept;
Re[4]: Оператор приведения
От: Videoman Россия http://www.htsproduction.com/
Дата: 05.07.19 09:51
Оценка:
Здравствуйте, rg45, Вы писали:

R>Ну это понятно почему — в цепочке присутствуют два последовательных пользовательских преобразования — оператор приведения Container и преобразующий конструктор optional, тогда как стандарт позволяет только одно. Тут нужна либо специализированная конвертирующая функция, либо явное преобразование к int:


R>
R>const std::optional<int> v5(int(cont5));
R>


Ну вот мне пока не очень понятно . Почему в той же ситуации стандартный std::optional работает как и ожидается ? Там как-то хитро объявляется преобразующий конструктор optional(U&&), если я правильно вас понял. И потом:
const std::optional<int> v5(int(cont5));

совсем не тоже самое что:
const std::optional<int> v5(cont5);
http://www.gravatar.com/avatar/60560936caa07b944d4c3cecf1c06cc5?s=80&d=identicon
Отредактировано 05.07.2019 9:54 Videoman . Предыдущая версия . Еще …
Отредактировано 05.07.2019 9:53 Videoman . Предыдущая версия .
Re[4]: Оператор приведения
От: rg45 СССР  
Дата: 05.07.19 09:55
Оценка:
Здравствуйте, night beast, Вы писали:

NB>замени

NB>
NB>        template <typename OtherType = Type>
NB>        optional(compatible_t<OtherType>&& value) noexcept;
NB>

NB>на
NB>
NB>        template <typename OtherType = Type>
NB>        optional(OtherType&& value, std::decay_t<compatible_t<OtherType>>* ptr = 0) noexcept;
NB>


Ой, что-то я сомневаюсь, что это поможет — с учетом того, что compatible_t реализован через is_constructible. Заколдованный круг же.
--
Самая страшная мафия это дураки — у них везде свои люди.
Re[5]: Оператор приведения
От: night beast СССР  
Дата: 05.07.19 09:57
Оценка:
Здравствуйте, rg45, Вы писали:

NB>>на

NB>>
NB>>        template <typename OtherType = Type>
NB>>        optional(OtherType&& value, std::decay_t<compatible_t<OtherType>>* ptr = 0) noexcept;
NB>>


R>Ой, что-то я сомневаюсь, что это поможет — с учетом того, что compatible_t реализован через is_constructible. Заколдованный круг же.


по крайней мере компилер не ругается
в первом конструкторе вообще смысла немного, т.к. сфинае не работает.
к томуже is_constructible к другому типу идет
Отредактировано 05.07.2019 10:07 night beast . Предыдущая версия . Еще …
Отредактировано 05.07.2019 9:58 night beast . Предыдущая версия .
Re[4]: Оператор приведения
От: Videoman Россия http://www.htsproduction.com/
Дата: 05.07.19 10:02
Оценка:
Здравствуйте, night beast, Вы писали:

NB>замени

NB>
NB>        template <typename OtherType = Type>
NB>        optional(compatible_t<OtherType>&& value) noexcept;
NB>

NB>на
NB>
NB>        template <typename OtherType = Type>
NB>        optional(OtherType&& value, std::decay_t<compatible_t<OtherType>>* ptr = 0) noexcept;
NB>


Сработало!!! Вот спасибо вам большое! Был бы очень вам признателен если вы объясните ваше заклинание ???
Для себя, я так понимаю что мы накладываем определенные ограничения (концепт для бедных, так сказать) на передаваемый типы, и данный конструктор по SFINAE выпадает из части перегрузок, но хотелось бы понять точно в чем была моя ошибка.
http://www.gravatar.com/avatar/60560936caa07b944d4c3cecf1c06cc5?s=80&d=identicon
Re[5]: Оператор приведения
От: night beast СССР  
Дата: 05.07.19 10:07
Оценка:
Здравствуйте, Videoman, Вы писали:

V>Сработало!!! Вот спасибо вам большое! Был бы очень вам признателен если вы объясните ваше заклинание ???

V>Для себя, я так понимаю что мы накладываем определенные ограничения (концепт для бедных, так сказать) на передаваемый типы, и данный конструктор по SFINAE выпадает из части перегрузок, но хотелось бы понять точно в чем была моя ошибка.

compatible_t отсекает левые варианты, но чтобы оно сработало, ему нужно подать тип, который пришел из параметров.
находясь первым аргументом он этот тип никак не получит.
std::decay просто очищает тип от ссылки, чтобы можно было иметь указатель и не возиться с параметрами по умолчанию.
Re[5]: Оператор приведения
От: rg45 СССР  
Дата: 05.07.19 10:09
Оценка:
Здравствуйте, Videoman, Вы писали:

R>>Ну это понятно почему — в цепочке присутствуют два последовательных пользовательских преобразования — оператор приведения Container и преобразующий конструктор optional, тогда как стандарт позволяет только одно. Тут нужна либо специализированная конвертирующая функция, либо явное преобразование к int:


R>>
R>>const std::optional<int> v5(int(cont5));
R>>


V>Ну вот мне пока не очень понятно . Почему в той же ситуации стандартный std::optional работает как и ожидается ? Там как-то хитро объявляется преобразующий конструктор optional(U&&), если я правильно вас понял.


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

В твоем случае конструктор не подхватывается потому, что его шаблоныый параметр просто не может быть выведен (потому что тип параметра конструктора это просто завуалированный XXX<YYY>::type — в принцие невыводимая конструкция) А значит и сам конструктор в принципе не может быль использован.
--
Самая страшная мафия это дураки — у них везде свои люди.
Отредактировано 05.07.2019 10:12 rg45 . Предыдущая версия .
Re[4]: Оператор приведения
От: Videoman Россия http://www.htsproduction.com/
Дата: 05.07.19 10:16
Оценка:
Здравствуйте, night beast, Вы писали:

Я поспешил на счет исправления. Код теперь компилируется, но ломается operator=, т.к. теперь постоянно срабатывает конструктор приведения типа.
http://www.gravatar.com/avatar/60560936caa07b944d4c3cecf1c06cc5?s=80&d=identicon
Re[5]: Оператор приведения
От: night beast СССР  
Дата: 05.07.19 10:22
Оценка:
Здравствуйте, Videoman, Вы писали:

V>Я поспешил на счет исправления. Код теперь компилируется, но ломается operator=, т.к. теперь постоянно срабатывает конструктор приведения типа.


код?
Re[6]: Оператор приведения
От: Videoman Россия http://www.htsproduction.com/
Дата: 05.07.19 10:24
Оценка:
Здравствуйте, rg45, Вы писали:

R>В твоем случае конструктор не подхватывается потому, что его шаблонный параметр просто не может быть выведен (потому что тип параметра конструктора это просто завуалированный XXX<YYY>::type — в принцие невыводимая конструкция) А значит и сам конструктор в принципе не может быль использован.


В поиске ответа я исхожу из того, что стандартный optional работает, значит есть кокой-то способ заставить этот синтаксис работать.
Мне кажется в стандартном optional конструктор преобразования optional(Other&& opt) не простой, а условный, он просто пропадает при разрешении перегрузки если мы пытаемся подать в конструктор optional не совместимый тип, и тогда уже срабатывает оператор преобразования типа, как-то так.
http://www.gravatar.com/avatar/60560936caa07b944d4c3cecf1c06cc5?s=80&d=identicon
Re[6]: Оператор приведения
От: rg45 СССР  
Дата: 05.07.19 10:24
Оценка:
Здравствуйте, night beast, Вы писали:

NB>к томуже is_constructible к другому типу идет


Да, точно. Что-то туплю я.
--
Самая страшная мафия это дураки — у них везде свои люди.
Re[7]: Оператор приведения
От: rg45 СССР  
Дата: 05.07.19 10:56
Оценка:
Здравствуйте, Videoman, Вы писали:

R>>В твоем случае конструктор не подхватывается потому, что его шаблонный параметр просто не может быть выведен (потому что тип параметра конструктора это просто завуалированный XXX<YYY>::type — в принцие невыводимая конструкция) А значит и сам конструктор в принципе не может быль использован.


V>В поиске ответа я исхожу из того, что стандартный optional работает, значит есть кокой-то способ заставить этот синтаксис работать.

V>Мне кажется в стандартном optional конструктор преобразования optional(Other&& opt) не простой, а условный, он просто пропадает при разрешении перегрузки если мы пытаемся подать в конструктор optional не совместимый тип, и тогда уже срабатывает оператор преобразования типа, как-то так.

Ну если ты безусловно добавишь конструктор optional(Other&&) у тебя тоже все заработает. Весь фокус в том, чтобы корректно выбросить этот конструктор из рассмотрения для несовместимых типов. Способ, предложенный night beast должен работать.
--
Самая страшная мафия это дураки — у них везде свои люди.
Re[6]: Оператор приведения
От: Videoman Россия http://www.htsproduction.com/
Дата: 05.07.19 10:57
Оценка:
Здравствуйте, night beast, Вы писали:

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


V>>Я поспешил на счет исправления. Код теперь компилируется, но ломается operator=, т.к. теперь постоянно срабатывает конструктор приведения типа.


NB>код?


В строчке 496 именно в VS2013 после копирования optCopy == 1, а не 1234. Я сейчас не очень красиво выгляжу, но я что-то не могу найти online компилятор для VS2013, и не могу вам это наглядно показать.
На VS2015, VS2017, VS2019 на GCC которые мне доступны ваше исправления отлично работает. В случае копирования optional вызывается оператор копирования, как я и ожидал. Я склоняюсь к тому что это опять глюк компилятора VS2013. На ней при копировании вызывается optional(OtherType&& value, std::decay_t<compatible_t<OtherType>>*) с параметром шаблона optional<int> ???
http://www.gravatar.com/avatar/60560936caa07b944d4c3cecf1c06cc5?s=80&d=identicon
Re[8]: Оператор приведения
От: Videoman Россия http://www.htsproduction.com/
Дата: 05.07.19 10:58
Оценка:
Здравствуйте, rg45, Вы писали:

R>Ну если ты безусловно добавишь конструктор optional(Other&&) у тебя тоже все заработает. Весь фокус в том, чтобы корректно выбросить этот конструктор из рассмотрения для несовместимых типов. Способ, предложенный night beast должен работать.


Да, это изначально и было моей идеей, но самому оказалось сложно правильно придумать такой "концепт".
http://www.gravatar.com/avatar/60560936caa07b944d4c3cecf1c06cc5?s=80&d=identicon
Re[7]: Оператор приведения
От: night beast СССР  
Дата: 05.07.19 11:12
Оценка: 6 (1)
Здравствуйте, Videoman, Вы писали:

NB>>код?


V>В строчке 496 именно в VS2013 после копирования optCopy == 1, а не 1234. Я сейчас не очень красиво выгляжу, но я что-то не могу найти online компилятор для VS2013, и не могу вам это наглядно показать.

V>На VS2015, VS2017, VS2019 на GCC которые мне доступны ваше исправления отлично работает. В случае копирования optional вызывается оператор копирования, как я и ожидал. Я склоняюсь к тому что это опять глюк компилятора VS2013. На ней при копировании вызывается optional(OtherType&& value, std::decay_t<compatible_t<OtherType>>*) с параметром шаблона optional<int> ???

возможны два варианта

1. заменить
optional& operator=(optional that) noexcept;
на
optional& operator=(const optional& that) noexcept;
optional& operator=(optional&& that) noexcept;

2. заменить (пишу на глаз)
std::decay_t<compatible_t<OtherType>>*
на
std::enable_if_t< compatible_v<OtherType> && !std::is_same_v<optional, std::decay_t<OtherType>> >*
Отредактировано 05.07.2019 11:13 night beast . Предыдущая версия .
Re[8]: Оператор приведения
От: Videoman Россия http://www.htsproduction.com/
Дата: 05.07.19 12:40
Оценка: 1 (1)
Здравствуйте, night beast, Вы писали:

NB>возможны два варианта


NB>1. заменить

NB>optional& operator=(optional that) noexcept;
NB>на
NB>optional& operator=(const optional& that) noexcept;
NB>optional& operator=(optional&& that) noexcept;

NB>2. заменить (пишу на глаз)

NB>std::decay_t<compatible_t<OtherType>>*
NB>на
NB>std::enable_if_t< compatible_v<OtherType> && !std::is_same_v<optional, std::decay_t<OtherType>> >*

Исправилось! Заменил compatible_t на такое:
template <typename OtherType>
using compatible_t = std::enable_if_t<
std::is_constructible<Type, std::remove_cv_t<OtherType>&&>::value && !is_same<optional, std::decay_t<OtherType>>::value, std::decay_t<OtherType>>;

Что исключило подстановки optional<OtherType> вместо оператора копирования.
Спасибо вам огромное!

P.S. Скорее бы переползти на C++17
http://www.gravatar.com/avatar/60560936caa07b944d4c3cecf1c06cc5?s=80&d=identicon
Отредактировано 05.07.2019 12:46 Videoman . Предыдущая версия . Еще …
Отредактировано 05.07.2019 12:40 Videoman . Предыдущая версия .
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.