разница между transform и and_then
От: sergii.p  
Дата: 11.08.25 16:34
Оценка: +2
тут в последнее время любят поднимать тему нелогичности в языке. Я продолжу эту славную традицию

struct Foo { 
    double val; 
    std::optional<double> opt;
};

int main()
{
    const std::optional<Foo> o = Foo{0, std::nullopt};
    return o.transform(std::mem_fn(&Foo::val)).value_or(0); //1
    return o.and_then(std::mem_fn(&Foo::opt)).value_or(0); //2
}


Попробуйте ответить, какие строчки некорректны с т.з. компилятора.
  Ответ:
Строчка 1 некорректна. transform не может принимать функцию, которая возвращает ссылку
Строчка 2 корректна. and_then на это ограничение глубоко фиолетово.
Как бы оба участка кода идейно похожи. Оба пропускают через себя внутренний член объекта, оба для экономии места используют mem_fn.
И теперь вопрос на засыпку: какого лешего?
Re: разница между transform и and_then
От: serg_joker Украина  
Дата: 11.08.25 20:40
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>тут в последнее время любят поднимать тему нелогичности в языке. Я продолжу эту славную традицию

Строго говоря, тут нелогичность(по крайней мере, логичного обоснования пока нет) в библиотеке, а не в языке, но вопрос всё равно хороший.
У меня не получилось ни найти обсуждение/обоснование в интернете, ни придумать своё.

Мне кажется, ходом мысли для transform могло бы быть нечто вроде: если мы из функтора, переданного в transform, возвращаем ссылку, то автор кода может ожидать, что дальнейший chaining будет работать именно с этим объектом, а не копией, что не так в силу того, что optional<T&> запрещён.

С другой стороны, автор кода функтора для and_then, который возвращает ссылку на optional, также может возлагать тщётные надежды на отсутствие копии...

нипанятна... ждём-с экспертов.
Отредактировано 11.08.2025 21:44 serg_joker . Предыдущая версия .
Re: разница между transform и and_then
От: watchmaker  
Дата: 11.08.25 22:55
Оценка: 12 (2)
Здравствуйте, sergii.p, Вы писали:

SP>transform не может принимать функцию, которая возвращает ссылку


Конечно, может.
Результатом будет optional<const double&>, и const double& является валидным параметром для optional: https://eel.is/c++draft/optional#optional.general-3

A type X is a valid contained type for optional if X is an lvalue reference type or ...

И вообще: [optional.optional.ref]]
Но ты наверное про старую версию языка пишешь, когда, действительно, специально было запрещено класть lvalue ref в optional.


SP>Как бы оба участка кода идейно похожи. Оба пропускают через себя внутренний член объекта, оба для экономии места используют mem_fn.


Они настолько же похожи, как указатель на массив похож на массив указателей. И правда — ведь и там, и там слова одинаковые! Эта аналогия, кстати, вполне уместна.

До вызова value_or в первой строке временный объект имеет тип optional<const double&>, а во второй строке — optional<double>.
Дальше, если в версии языка ссылки в optional класть запрещено, то будет ошибка компиляции.
Но сами методы optional тут в общем-то ни при чём: я и без transform/and_then всегда мог неаккуратно написать что-то вроде
template <class T>
std::optional<T> foo(T&& input) { 
  ... 
};

А потом "удивляться" почему работает вызов
foo(5);
, но сломан вызов:
int x = 5;
foo(x);

В твоём примере похожая ситуация, только ты её замаскировал рассуждениями про mem_fn и монадные операции
Re[2]: разница между transform и and_then
От: sergii.p  
Дата: 12.08.25 07:34
Оценка: +1
Здравствуйте, watchmaker, Вы писали:

W>Но ты наверное про старую версию языка пишешь, когда, действительно, специально было запрещено класть lvalue ref в optional.


не знаю что за "старая" версия. В С++23 этого нет. Когда ждать 26-ой и будет ли оно там — отдельный вопрос. Всё-таки коммитет держал optional в калечном состоянии довольно долго, чтобы вот так сразу взять и исправить один из главных косяков cppreference настаивает что пока

The type must meet the requirements of Destructible (in particular, array and reference types are not allowed).

Re[2]: разница между transform и and_then
От: serg_joker Украина  
Дата: 12.08.25 08:21
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Здравствуйте, sergii.p, Вы писали:


W>Но ты наверное про старую версию языка пишешь, когда, действительно, специально было запрещено класть lvalue ref в optional.

Я сначала обрадовался, а потом таки проверил, и у меня на актуальном компиляторе не получилось


Т.е. по факту пока таки нельзя.

Но у меня вопрос даже не про это, а про то, почему изначально результатом optional::transform сделали remove_cv_t, а не remove_cvref_t, ведь тогда бы transform позволял fn, возвращающий ссылку даже и на "старой версии языка".
Re[3]: разница между transform и and_then
От: watchmaker  
Дата: 12.08.25 10:05
Оценка: 2 (1) +1
Здравствуйте, serg_joker, Вы писали:

_>Я сначала обрадовался, а потом таки проверил, и у меня на актуальном компиляторе не получилось


Надо смотреть не столько на свежесть компилятора, сколько на версию __cpp_lib_optional.

_>Т.е. по факту пока таки нельзя.


Ага, поддержки в популярных реализациях STL пока ещё нет.

_>Но у меня вопрос даже не про это, а про то, почему изначально результатом optional::transform сделали remove_cv_t, а не remove_cvref_t


Кстати, изначально в proposal так и было. Потом при обсуждении дефект исправили.

_> ведь тогда бы transform позволял fn, возвращающий ссылку даже и на "старой версии языка".


А цель-то какая? Материализовать ссылки ценой ещё большего замедления optional? В нём и так уже не очень хорошая ситуация с лишними копированиями.

Согласен, что пока используешь простые типы, то невозможность написать o.transform(&Foo::val) сильно раздражала. Но для optional<Database> вред от незаметных копирований уже может перевешивать выигрыш от короткой записи.

Не знаю какой аргумент стал решающим, но итоговый вариант с сохранением ссылок мне нравится куда больше.
Re[4]: разница между transform и and_then
От: serg_joker Украина  
Дата: 12.08.25 10:32
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>А цель-то какая? Материализовать ссылки ценой ещё большего замедления optional? В нём и так уже не очень хорошая ситуация с лишними копированиями. <и далее>

Я же не оспариваю, мне интересно, каковы причины такого поведения, выбранного коммитетом. Я предполагал, что это защита от неожиданного копирования, но не нашёл тому подтверждения.
Верю на слово, что так и есть.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.