LINUX.ORG.RU

std::optional<T> vs специальные значения.

 


1

5

Добрый день.

Рассмотрим пример из Boost:

// Here, having received an empty vec and having no size_t to return is not a failure but a normal, albeit irregular, situation.
template <typename T>
optional<size_t> find_smallest_elem(const std::vector<T>& vec);

Теперь посмотрим на std::string:

// Returns: xpos if the function can determine such a value for xpos. Otherwise, returns npos.
size_type find(const basic_string& str, size_type pos = 0) const noexcept;

Внимание, вопросы:

  1. Какой API вам кажется приятнее?
  2. Если да, то чем это лучше подхода std::string::npos?
  3. Если это лучше подхода std::string::npos, то чем думали члены комитета при выпуске C++17, где же этот их самый optional в API того же std::string?

Спасибо.

Ответ на: комментарий от pon4ik

Думали очевидно обратной совместимостью.

А что мешает добавить функции в стиле «современный API», он же «modern C++ style»?

azelipupenko
() автор топика
Ответ на: комментарий от pon4ik

Правила перегрузки функций?

Ну добавили же, например, std::is_same_v<T> к имеющемуся std::is_same<T>::value, и ничего, правила перегрузки не помешали.

azelipupenko
() автор топика
Ответ на: комментарий от azelipupenko

Мысль конечно интересная, но имхо внешний функтор(х3 есть ли уже) более приятный вариант, чем добавление синтаксиса с вариантами:

auto pos = str.find("what", pos);
auto pos_o = str.find_o("what", pos);

По сути, если обьявить метод deprecated и рекомендовать везде использовать find_o - это предложение лезть два раза в кодовую базу, когда и так всё работает и не имеет каких то явных проблем с дизайном.

pon4ik ★★★★★
()
Ответ на: комментарий от pon4ik

когда и так всё работает и не имеет каких то явных проблем с дизайном

А сам то в каком-нибудь своём новом API какой вариант выбрал бы? Стал бы заморачиваться с optional?

azelipupenko
() автор топика
Ответ на: комментарий от azelipupenko

А каким боком is_same_v идёт к is_same в данном контексте? Это в принципе не функции, и уж тем более не члены класса. Просто шоткат использующий фишку обкатанную в 14ом.

pon4ik ★★★★★
()
Ответ на: комментарий от pon4ik

А каким боком is_same_v идёт к is_same в данном контексте? Это в принципе не функции, и уж тем более не члены класса.

Таким боком, что проблем добавить что-либо в стандартную библиотеку нет. В т.ч. и функции, и классы.

azelipupenko
() автор топика
Ответ на: комментарий от azelipupenko

Возможно, но конкретно в этом случае маловероятно. Если только проверка на npos не была бы частью какой то дюже мощной шаблонной логики в купе с другими возможностями пустых исходов. Но это скорее характерно для чтения значения обьекта целиком во всяких конфигах, а разница проверки на npos и на has_value легко прячется в предикат.

pon4ik ★★★★★
()
Ответ на: комментарий от pon4ik

Возможно, но конкретно в этом случае маловероятно.

Но ведь это тот самый случай, который описан в документации boost::optional: It is recommended to use optional<T> in situations where there is exactly one, clear (to all parties) reason for having no value of type T, and where the lack of value is as natural as having any regular value of T.

azelipupenko
() автор топика
Ответ на: комментарий от azelipupenko

Вот разница между для is_same и is_same_v:

if (is_same_v(a, b)::value) throw "Much cute, but impossible in 11x and forgoten in 14x";

if (is_same<decltype(a),decltype(b)>()::value) throw "Less cute but will work";

А вот разница между твоими вариантами:

if (string::npos == find("abc", 0)) throw "Well known, robust";
if (!find("abc", 0).has_value()) throw "Little bit shorter, need to remember, need to port code to be 21x compatible in 21th";
pon4ik ★★★★★
()
Последнее исправление: pon4ik (всего исправлений: 2)
Ответ на: комментарий от pon4ik

А вот разница между твоими вариантами:

Вообще-то я думал про вот так:

if (auto pos = find("abc", 0)) {
  // работаем с pos
}

Сейчас же я должен утомлять себя и читателя писаниной:

if (auto pos = find("abc", 0); pos != string::npos) {
  // работаем с pos
}
azelipupenko
() автор топика

Естественно optional это корректный подход с точки зрения семантики, где сам возвращаемый тип говорит, что значение может быть, а может не быть. И попытка разыменования такого optional без проверки гарантированно приведёт к assert'у или исключению.

В то время как npos без писка позволит вам замусорить память чем-то вроде:

auto pos = mystring.find("foo");
printf("right string part: %s", mystring.c_str() + pos);

И дебажить такой код вы будете месяцами, в то время как optional укажет на ошибку на раннем этапе. Но это по сути не так важно, главное это, опять же, семантика, когда в самом типе заложена информация о доступных над ним операциях. Простыми словами: вы не сможете совершить неправильную операцию над переменной, если самой переменной не существует. Это один их принципов RAII — переменная или в валидном состоянии или её нет.

С точки зрения производительности optional можно специлизировать, чтобы он не занимал больше памяти, хранил только size_t и кастился к false, если значение равно npos.

Dendy ★★★★★
()
Ответ на: комментарий от azelipupenko

Да, так выглядит твой вопрос резонней.

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

pon4ik ★★★★★
()
Ответ на: комментарий от Dendy

Естественно optional это корректный подход с точки зрения семантики, где сам возвращаемый тип говорит, что значение может быть, а может не быть.

Вот вот. Можно обойтись без optional, как это делают в C, но ведь в C++ уже есть optional. Может быть в случае std::string::npos ещё стоял остро вопрос производительности? Хотя, operator* не проверяет optional на наличие значения, так что просадки быть не должно.

попытка разыменования такого optional без проверки гарантированно приведёт к assert'у или исключению.

Уточнение: только если использовать value(), но не operator*, который «does not check whether the optional contains a value! You can do so manually by using has_value() or simply operator bool(). Alternatively, if checked access is needed, value() or value_or() may be used.»

azelipupenko
() автор топика

Если это лучше подхода std::string::npos, то чем думали члены комитета при выпуске C++17, где же этот их самый optional в API того же std::string?

Теперь ты понимаешь, почему разработка коммитетом — это очень плохо?

hateyoufeel ★★★★★
()
Ответ на: комментарий от hateyoufeel

Теперь ты понимаешь, почему разработка коммитетом — это очень плохо?

Не понимаю. Кстати, а как надо разрабатывать, чтобы было очень хорошо?

azelipupenko
() автор топика

1. optional всегда лучше специальных значений

2.
а) Не всегда есть возможность найти значение, которое не может вернуть функция/метод. Значит подход std::string::npos не будет работать для всех функций/методов. Лучше иметь один подход, чем два.
б) optional фикцирует как часть контракта тот факт, что значение может быть и не вернется.

3. Завезут со верменем и вариант с optional. Обратную совместимость никто ломать не будет.

trex6 ★★★★★
()
Ответ на: комментарий от azelipupenko

Напиши в комитет пропосал. Будет что в резюме добавить.

trex6 ★★★★★
()
Ответ на: комментарий от pon4ik

ЧЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕ

У тебя оба варианта не скомпилятся.

if (is_same<decltype(a),decltype(b)>::value) throw "Much cute, but impossible in 11x and forgoten in 14x";

if (is_same_v<decltype(a),decltype(b)>) throw "Less cute but will work";

Нет в std такой функции как is_same.

Kuzy ★★★
()
Последнее исправление: Kuzy (всего исправлений: 1)

1 - optional; 2 -тем, что труднее сделать малозаметную ошибку; 3 - о привычке.

tailgunner ★★★★★
()

Какой API вам кажется приятнее?

Конечно optional, ведь это шажок в сторону АлгТД.

Если да, то чем это лучше подхода std::string::npos?

string::npos это вообщая лютая хрень в духе чистой сишки, а у нас тут высокоуровневый язык, ёпта

чем думали члены комитета при выпуске C++17

Ревизия каждой отдельно взятой функции в std на соответствие новым фишечкам это тот ещё труд, видимо банально не хватило рук

Crocodoom ★★★★★
()
Ответ на: комментарий от Crocodoom

видимо банально не хватило рук

Скорее их заломало делать find2. Хотя, в духе Комитета будет добавить search или look_for :D

И потом, логично тогда то же самое сделать для всех STL-ных find, которые возвращают end().

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.