Pull to refresh

Comments 16

С моей т.з. у std::optional один небольшой недостаток. Нет поддержки sentinel value, т.е. когда какое-то значение исходного типа считается nothing. Это позволило бы, к примеру, паковать указатели или сырые хендлы из сторонних библиотек без дополнительного флага, а следовательно совместимо по бинарному представлению с исходным типом. Как следствие — можно было бы делать красивый интерфейс для низкоуровневых библиотек без доп. перепаковки памяти.

На самом деле, автор лукавит. Финальный вариант кода потерял кой-какие свойства, которые были у изначального кода с сырыми указателями:

1. Вызывающая сторона могла вместо out-параметра передать nullptr, тем самым сказав «мне это не нужно, можешь не вычислять»
2. Позитивный code path вызывающего кода растет *вглубь* отступов, вместо того чтобы оставаться на верхнем уровне, используя вложенные блоки для короткой обработки побочных неуспешных сценариев

И почему-то в C++ такое постоянно. Вводятся новые модные плюшки, которые вроде бы как бы должны заменять старые олдскульные подходы, многие из которых тянутся со времен pure C. Но всё время оказывается что новые фишки покрывают не все случаи, которые были возможны старыми способами, поэтому полностью от них отказаться нельзя.
Не увидел по ссылке ничего относящегося к тому что я сказал. Там занимаются другой проблемой — в некоторых случаях использование std:optional даёт существенный оверхед по памяти.
Идея иметь compact_optional который будет занимать столько же места.
И использовать примерно так:
using opt_count = compact_optional<evp_int<int, -1>, class cnt_tag>;
Здравствуйте!

Мне кажется, Вы тоже немного лукавите. Тот факт, что существует std::optional не означает, что его следует использовать всегда и везде:

  1. Если требуется иметь описанное Вами поведение, то std::optional не нужен. Если же очень хочется, то всегда можно оставить логику, связанную с nullptr: в случае, если результат не интересен, передавать nullptr в качестве значения std::optional для опционального выходного аргумента (хотя это и костыль). Отдельно хотелось бы отметить, что может иметь смысл создание перегрузки, которая не принимает этот самый выходной аргумент, который может быть иногда не нужен — если результат часто бывает не нужен, то такой вариант сократит возможность возникновения ошибки. (Если я не ошибаюсь, выходные аргументы в современном C++ считаются немного не актуальными в общем случае, но у меня нет под рукой ссылок для подтверждения.)
  2. Мне кажется, углубление позитивной ветки кода логично, если данная ветка должна работать со значением, которого может не быть. Винить в этом нововведения не разумно — раньше Вам точно также пришлось бы проверять, вернулось ли что-то, если оно могло не вернуться. В случае же, когда std::optional используется для возврата значений, которые должны быть возвращены всегда, в коде могут существовать более серьёзные проблемы, чем лишний уровень вложенности.


Не забывайте, что C++ — это, в первую очередь, инструмент. И добавление в него «новых модных плюшек» вовсе не означает, что они должны полностью заменять то, что уже есть в языке. Вот если бы наряду с добавлением, скажем, std::function удалили бы возможность использования указателей на функции, тогда можно было бы говорить о том, что новые возможности ущербны, ведь они что-то там не позволяют делать.
2. углубление позитивной ветки плохо с точки зрения читаемости. Не просто так в Rust сделали макрос try!
Плохо, конечно. Но:
  • Если значение является не обязательным, то мне всё ещё кажется вполне логичным написать if с проверкой его существования и обработать его в соответствующей блоке кода.
  • Если в обработка опционального значения увеличивает глубину вложенности на 1 — это не страшно в общем случае. По-настоящему плохо становится тогда, когда это уже 3-5-8 уровень вложенности. Но в этом случае проблема уже явно не в необходимости проверки чего-либо, а в низком качестве кодовой базы (Линус как-то высказывался на этот счёт в выражениях типа «your code is already fucked up», если мне не изменяет память)
  • Если очень хочется и есть такая возможность, то можно использовать ранний возврат:
    auto optional_value = function_returning_optional();
    
    if (!optional_value) {
    	return;
    }
    
    auto& my_precious_value = *optional_value;
    
    // Great code that cannot be put under "if"
  • Если в блоке кода может появится много опциональных значений, то возможно проблема в самом коде, надо что-то исправить.
1. Для этого предпочтительнее использовать перегрузку функций. Если у вас есть функция с набором опциональных null-параметров, то, возможно, вам пора подумать о рефакторинге кода, потому что скорее всего такая функция нарушает принцип единственной ответственности.

2. Никто не мешает проверять optional на отсутствие значений, так же как и указателей на nullptr. Мне кажется, это скорее дело вкуса и стиля, а не новомодных плюшек.

Я с вами асбсолютно согласен! Как подумаю, что какую массу возможностей теряю, перестав использовать void *, так буквально плакать хочется…
По поводу первого пункта, как насчет такого варианта?
godbolt.org/g/4XsWU3

Можно не просто не вычислять то, что не нужно, но и даже проверки в конечный код не попадет. Как бонус можно задать вычисление только самого последнего аргумента, не перечисляя всех предыдущих, как в случае с out-параметрами.
Мне статья рассказывает что вот меняем аут-параметры на optional и становится хорошо. Умалчивая, что не всегда и не всё становится лучше при такой замене. В этом и состоит моя претензия.
Вроде всё понял, не понял только зачем вот это:
bool anyCivilUnits { false };
bool anyCombatUnits {false};
int numAnimating { 0 };



Чем тут фигурные скобки лучше обычной инициализации? Просто новая мода какая-то?
Небольшая разница есть:

struct A
{
 int a = 1.0; // Компилируется
 int c{2.0}; // Получаем по рукам от компилятора
};
ну у меня
int a = 1.0;
дает
warning C4244: 'initializing': conversion from 'double' to 'int', possible loss of data

т.е. это для тех, кому на предупреждения наплевать? понятно.
Спасибо за перевод. К сожалению, некоторые плюсовые фишки, заимствованные из других экосистем, неполны и не везде консистентны. Если бы в плюсах была поддержка монад, тип optional был бы на порядок удобнее, а так же другие типы, имещие монадические свойсива (expected, future, корутины).

Я бы хотел высказать одну смелую мысль: ничто так не помогает в изучении современных «плюсов» (вот такие типы + шаблонная магия), как изучение чуточки Haskell.
bool anyCivilUnits { false };
bool anyCombatUnits {false};
int numAnimating { 0 };

вырвиглазное форматрирование

Sign up to leave a comment.

Articles