Здравствуйте, gbear, Вы писали:
EP>>Явно или автоматически: Variant<Variant<T, void>, void> -> Variant<T, void>
G>А явно это как? Особенно интересно, как это "явно" будет выглядеть в обобщенном коде.
Например
simplify(v) или какой-нибудь унарный оператор
^^ v
G>А если автоматически, то получается тип Variant<Variant<T, void>, void> существовать не может? Я вас правильно понимаю?
Хм, наверное да. Точнее существовать может, но до первого использования.
G>>>Почему только при присвоении?! Везде, где "по смыслу" должно быть T.
EP>>Вот тут:
EP>>EP>>auto foo()
EP>>{
EP>> return (if(c) T{1} else T{2});
EP>>}
EP>>
EP>>какой должен быть тип результата у foo?
G>Очевидно — variant.
Variant[T]? А как же:
G>>>Почему только при присвоении?! Везде, где "по смыслу" должно быть T.
Что имелось в виду? Протягивать Variant[T] до T? А если пользователь просто захочет позвать метод T?
G>Хотите работать с res как c variant'ом — ктож вам запретит Но если у вас есть, например, какая-нибудь ф-ция от X, то, согласитесь, удобней "уметь" в неё передавать variant[X] "as is", а не дергать какие-то unbox'ы.
Можно и так — автоматически/неявно.
G>>>В нашем случае использовать if не получится...
EP>>Почему?
G>?! Очевидно, потому что if вернет нам новый variant. С которым "нужно что-то делать"
Когда из if возвращается указатель на какую-нибудь абстрактную базу, при работе с ней придётся точно также выяснять — что же там внутри, и какой именно метод вызывать. Выбор-то никуда не исчезает
G>>>конечно, если вы не предлагаете "нафантазировать" для этого случая еще один — специальный — "if". Ну, который не будет вычисляться в variant .
EP>>Зачем?
G>?! Вы серьезно не понимаете? ?! Очевидно, потому что "нормальный" if вернет нам новый variant. С которым "нужно что-то делать"
А сейчас возвращается общая база, с которой тоже "нужно что-то делать".
G>Это пример из boost, в который вы мне тыкали, как в пример. И таки да... это называется, двойная диспетчеризация.
Там одинарная диспетчеризация, а не двойная
G>>>?! Вы вообще о чем? Как определить этот "метод"? Смысл использования variant'а — по вашей же логике — "впихнуть невпихуемое". Если мы подразумеваем общий интерфейс, то зачем variant-то?!
EP>>Я уже приводил пример, придётся скопировать:
EP>>EP>>...
EP>>
EP>>Здесь нет общего предка у Widget и Gadget (да и вообще у них нет ни одного метода), а интерфейс использования в данном случае у них одинаковый
G>Я где-то говорил за "общего предка"?! Процитирую себя Если мы подразумеваем общий интерфейс, то зачем variant-то?!
Тогда покажите как этот пример (где у разных типов общий интерфейс) переделать без variant'а и без общего предка.
G>Что мешает-то на лету "собрать" нужный тип (в вашем примере, это агрегат Widget и Gadget), реализующий _нужный_ интерфейс?!
Пример сборки нужного типа в студию. Мне кажется это будет сложнее чем variant.
G>>>Проблему можно выразить так: Если результат if можно обработать только через PM, то зачем нам использовать if? Почему сразу не использовать PM?!
EP>>Во-первых, не только через PM.
G>Возвращаемся к нашим баранам. Таки через visitor'ы предлагаете?
Один из вариантов. Вот только visitor может иметь один полиморфный метод, а может много перегрузок — а-ля PM, а может и то и то.
G>Повторюсь — обработка через if даст вам на выходе опять variant. Т.е. получаем "один раз variant — всегда variant". Оно действительно надо?
С общим предком тоже самое. Если же обработать всё что относится к этой ветке, внутри неё, и наружу никогда не выдавать полиморфный тип (будь то variant или абстрактная база), то мы очень быстро получим комбинаторный взрыв.
G>>>Напоминаю, в сабже if — это "сахар" для PM.
EP>>И что? Это мешает построить if возвращающий variant?
G>Да ничего не мешает. Вопрос не в этом. Вопрос в том, чего вы этим хотите добиться? Пресловутой "симметрии" с if-без-else, как вроде бы выяснили, добиться таким образом не получится. Так в чем смысл-то?
Как это не получится, если выяснили что как раз получается
EP>>Чем код выше, принципиально отличается от следующего:
EP>>EP>>MATCH( IF(x>0) { return getA(); } ELSE { return getB(); } )
EP>> CASE(y IS A) { handleA(y.PropertyOfA); }
EP>> CASE(y IS B) { handleB(y.MethodOfB()); }
EP>>;
EP>>
EP>>
G>Принципиально — ничем. Только какое он имеет отношение к?
Тем что такой синтаксис можно получить на базе boost::variant