// Вариант 1
x([](string const& s) { ... })
// Вариант 2
x([](auto const& s) { ... })
А теперь проблемы:
Из-за того, что x принимает абсолютно любой тип, нет никаких подсказок какую лямбду вообще нужно писать.
Т.е. подсказок от среды разработки и компилятора не будет никаких.
Я хотел бы как-то в сигнатуре "x" сказать, что мне нужен любой функтор принимающий std::string.
Можно конечно через enable_if , но думается, что должен быть метод получше.
Вариант 2 добавляет ещё больше хлопот в поддержке.
Тут мало того что не видно типа, так и ещё не видно что ожидается.
Такой код писать тяжело, а про поддержку вообще молчу.
В идеале хотелось написать вот так и компилятор сам всё сделает и среда даст подсказки где надо.
Возможно уже есть предложения в стандарт ?
_NN>Из-за того, что x принимает абсолютно любой тип, нет никаких подсказок какую лямбду вообще нужно писать. _NN>Т.е. подсказок от среды разработки и компилятора не будет никаких. _NN>Я хотел бы как-то в сигнатуре "x" сказать, что мне нужен любой функтор принимающий std::string. _NN>Можно конечно через enable_if , но думается, что должен быть метод получше.
С другой стороны, в алгоритмах стандарной библиотеки ровно те же проблемы. Поэтому если ни enable_if, ни std::function не нравятся, остается только один вариант — смириться.
В некоторых частных случаях, когда x — это враппер к f, может оказаться полезным указать явно тип возвращаемого значения, пользуясь std::result_of:
Здравствуйте, _NN_, Вы писали:
_NN>Нонеча тут пишу на современном C++, и понял, что я с лямбдами я что-то не понимаю.
Это относится практически ко всему шаблонно-обобщённому коду, а не только к лямбдам.
_NN>Вариант 2 добавляет ещё больше хлопот в поддержке. _NN>Тут мало того что не видно типа, так и ещё не видно что ожидается. _NN>Такой код писать тяжело, а про поддержку вообще молчу.
Это считай duck typing времени компиляции. В динамически типизированных языках типа Python тоже duck typing, только вместо ошибок компиляции — ошибки выполнения, и ничего — там тоже есть жизнь.
_NN>В идеале хотелось написать вот так и компилятор сам всё сделает и среда даст подсказки где надо. _NN>Возможно уже есть предложения в стандарт ?
Конечно, и даже не одна версия, разных уровней навороченности. Называется "концепции"/concepts — вкратце это описание требований к типам, которые передаются в шаблоны. В некоторых вариациях это не просто требования к типам, но ещё и интерфейс к ним — в этом случае код шаблонов использующих типы с таким интерфейсом будет фейлится ещё до instantiation в случае нарушения обозначеного интерфейса (что способствует раненому обнаружению ошибок).
Из-за них сильно задержался выход C++0x, который в итоге оказался C++11, но они до сих пор не вошли (к C++20 уже есть надежда).
А пока их нет — можно использовать комментарии/документацию (смотри например STL), внятные имена типов (например UnaryPredicate), а также разного рода костыли/эмуляции концепций а-ля Boost.ConceptCheck
_NN>Из-за того, что x принимает абсолютно любой тип, нет никаких подсказок какую лямбду вообще нужно писать. _NN>Т.е. подсказок от среды разработки и компилятора не будет никаких. _NN>Я хотел бы как-то в сигнатуре "x" сказать, что мне нужен любой функтор принимающий std::string. _NN>Можно конечно через enable_if , но думается, что должен быть метод получше.
Кстати, а чего SFINAE не нравится? Синтаксический сахарок подмешать и вполне съедобно (не компилял):
Проблема тут в том, что это нужно написать , тестиповать , потом поддерживать и наконец расписать комментариями как эта магия работает
Неясно почему нет из коробки и почему нет встроенного синтаксиса как в других языках программирования .
Здравствуйте, _NN_, Вы писали:
_NN>Я хотел бы как-то в сигнатуре "x" сказать, что мне нужен любой функтор принимающий std::string. _NN>Возможно уже есть предложения в стандарт ?
Можно (будет) выразить концептами:
_NN>void x(f: (std::string const&) -> void)
Замечу, что такой синтаксис плох тем, что решает уж больно частную задачу. Например, не всегда важно какое значение возвращает функция (не страшно, что там может быть не void). Или не всегда важно чтобы функция принимала именно std::string, а не любой неявно приводимый к нему тип. А иногда наоборот — нужно совсем жестко ограничить тип и запретить любый преобразования.
Или, например, может потребоваться ограничить более одного аргумента. Скажем, часто требуется передать функцию, которая принимает два параметра любого, но одинакового типа (подобно последнему аргументу в std::accumulate).
Как расширить синтаксис этой конструкции на все эти случаи, не сделав его при этом совсем уж ужасным, — совершенно не ясно.
Так что таких конструкций в языке ждать не стоит. Тем более, что концепты уже предложили альтернативу.
_NN>>Так лямбду же нельзя класть в decltype . BFE>Это пока. А since C++20 будет можно.
Да, с C++20 лямбды похоже будут разрешены в unevaluated context. Вот только как это поможет автору проблемы наложить ограничения на параметры? Правильно — никак
Здравствуйте, watchmaker, Вы писали:
_NN>>>Так лямбду же нельзя класть в decltype . BFE>>Это пока. А since C++20 будет можно. W>Да, с C++20 лямбды похоже будут разрешены в unevaluated context. Вот только как это поможет автору проблемы наложить ограничения на параметры? Правильно — никак
Это не будет работать по многим причинам.
Cоветую прочитать P0315R4 — там помимо простого "разрешить лямбды в unevaluated context" написано и про то, как это будет взаимодействовать со всем остальным языком. И вообще, зачем это надо и что можно делать.
Для начала, в первой строке присуствует шаблонный тип T. Во второй строке же написано просто OnlyStrings. Вопрос, куда пропало T? Тип-то OnlyStrings<something>. И как его выводить?
Далее, какую сигнатуру имеет функция x? Можно сразу рассмотреть с позиции linker'а. Что он увидет для такой функции? Спойлер: в P0315 — это описано как "nightmare" (но это мелочь и обходится, если x сделать internal linkage).
Ну и главное даже не это, а то, что каждая лямбда порождает свой уникальный тип. То есть аргументом функции может быть только именно та же самая лямбда, которая была внутри decltype. Не другая лямбда с такой же сингнатурой, и тем более не другое замыкание или функция, которую можно вызвать с аргументом const std::string&. Подходит только оригинальная лямбда — та самая, у которой пустое тело. (а если бы в C++20 не добавили default конструктор к лямбдам без захвата, то функцию x вообще бы было невозмножно вызвать, так как единственный объект подходящего типа присутствовал только внутри decltype).
Здравствуйте, watchmaker, Вы писали:
W>Ну и главное даже не это, а то, что каждая лямбда порождает свой уникальный тип. То есть аргументом функции может быть только именно та же самая лямбда, которая была внутри decltype. Не другая лямбда с такой же сингнатурой, и тем более не другое замыкание или функция, которую можно вызвать с аргументом const std::string&. Подходит только оригинальная лямбда — та самая, у которой пустое тело.
Не, ну так не интересно. Я думал будет выдан "обобщённый" тип на callable сигнатуру или хотя бы тип подходящий для спектра лямбда функций с одинаковыми параметрами...
Здравствуйте, B0FEE664, Вы писали:
BFE>Не, ну так не интересно. Я думал будет выдан "обобщённый" тип на callable сигнатуру или хотя бы тип подходящий для спектра лямбда функций с одинаковыми параметрами...
Здравствуйте, watchmaker, Вы писали:
BFE>>Не, ну так не интересно. Я думал будет выдан "обобщённый" тип на callable сигнатуру или хотя бы тип подходящий для спектра лямбда функций с одинаковыми параметрами... W>Кажется, std::function<R(T)> уже придумали
Ну да. Вот если decltype от лябды будет выдавать тип std::function<R(T)>, то какие будут проблемы окромя производительности?
Здравствуйте, B0FEE664, Вы писали:
BFE>Здравствуйте, watchmaker, Вы писали:
W>>Кажется, std::function<R(T)> уже придумали :)
BFE>Ну да. Вот если decltype от лябды будет выдавать тип std::function<R(T)>, то какие будут проблемы окромя производительности?
Э... я выше про std::function<R(T)> пошутил, если что. Надеюсь ты тоже не всерьёз.
Здравствуйте, watchmaker, Вы писали:
W>>>Кажется, std::function<R(T)> уже придумали BFE>>Ну да. Вот если decltype от лябды будет выдавать тип std::function<R(T)>, то какие будут проблемы окромя производительности? W>Э... я выше про std::function<R(T)> пошутил, если что. Надеюсь ты тоже не всерьёз.
Почти. Я не вижу причин, почему один тип лямбды не может приводится к другому типу лямбды, если у них одинаковая сигнатура.