Указать тип параметра у функтора
От: _NN_ www.nemerleweb.com
Дата: 26.07.18 14:17
Оценка:
Нонеча тут пишу на современном C++, и понял, что я с лямбдами я что-то не понимаю.
Допустим имеем такой код:

template<typename F>
void x(F f)
{
 std::string s;
 F(s);
}


И использование
// Вариант 1
x([](string const& s) { ... })

// Вариант 2
x([](auto const& s) { ... })


А теперь проблемы:

  • Из-за того, что x принимает абсолютно любой тип, нет никаких подсказок какую лямбду вообще нужно писать.
    Т.е. подсказок от среды разработки и компилятора не будет никаких.
    Я хотел бы как-то в сигнатуре "x" сказать, что мне нужен любой функтор принимающий std::string.
    Можно конечно через enable_if , но думается, что должен быть метод получше.

  • Вариант 2 добавляет ещё больше хлопот в поддержке.
    Тут мало того что не видно типа, так и ещё не видно что ожидается.
    Такой код писать тяжело, а про поддержку вообще молчу.

    В идеале хотелось написать вот так и компилятор сам всё сделает и среда даст подсказки где надо.
    Возможно уже есть предложения в стандарт ?
    void x(f: (std::string const&) -> void)
    {
     std::string s;
     F(s);
    }


    Если что std::function это не решение проблемы.
  • http://rsdn.nemerleweb.com
    http://nemerleweb.com
    Re: Указать тип параметра у функтора
    От: rg45 СССР  
    Дата: 26.07.18 14:43
    Оценка:
    Здравствуйте, _NN_, Вы писали:

    _NN>
    _NN>template<typename F>
    _NN>void x(F f)
    _NN>{
    _NN> std::string s;
    _NN> F(s);
    _NN>}
    _NN>


    _NN>Из-за того, что x принимает абсолютно любой тип, нет никаких подсказок какую лямбду вообще нужно писать.

    _NN>Т.е. подсказок от среды разработки и компилятора не будет никаких.
    _NN>Я хотел бы как-то в сигнатуре "x" сказать, что мне нужен любой функтор принимающий std::string.
    _NN>Можно конечно через enable_if , но думается, что должен быть метод получше.

    С другой стороны, в алгоритмах стандарной библиотеки ровно те же проблемы. Поэтому если ни enable_if, ни std::function не нравятся, остается только один вариант — смириться.

    В некоторых частных случаях, когда x — это враппер к f, может оказаться полезным указать явно тип возвращаемого значения, пользуясь std::result_of:

    template<typename F>
    std::result_of_t<F(const std::string&)> x(F&& f)
    {
      std::string s;
      return f(s);
    }
    --
    Не можешь достичь желаемого — пожелай достигнутого.
    Re: Указать тип параметра у функтора
    От: Evgeny.Panasyuk Россия  
    Дата: 26.07.18 14:54
    Оценка: +2
    Здравствуйте, _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
    Отредактировано 26.07.2018 15:04 Evgeny.Panasyuk . Предыдущая версия . Еще …
    Отредактировано 26.07.2018 15:03 Evgeny.Panasyuk . Предыдущая версия .
    Отредактировано 26.07.2018 14:59 Evgeny.Panasyuk . Предыдущая версия .
    Re: Указать тип параметра у функтора
    От: rg45 СССР  
    Дата: 26.07.18 14:58
    Оценка: 10 (1)
    Здравствуйте, _NN_, Вы писали:

    _NN>
    _NN>template<typename F>
    _NN>void x(F f)
    _NN>{
    _NN> std::string s;
    _NN> F(s);
    _NN>}
    _NN>


    _NN>Из-за того, что x принимает абсолютно любой тип, нет никаких подсказок какую лямбду вообще нужно писать.

    _NN>Т.е. подсказок от среды разработки и компилятора не будет никаких.
    _NN>Я хотел бы как-то в сигнатуре "x" сказать, что мне нужен любой функтор принимающий std::string.
    _NN>Можно конечно через enable_if , но думается, что должен быть метод получше.

    Кстати, а чего SFINAE не нравится? Синтаксический сахарок подмешать и вполне съедобно (не компилял):

    template <typename T, typename ReturnType = void>
    using assume_signature = std::enable_if_t<std::is_same<std::result_of_t<T>, std::result_of_t<T>>, ReturnType>;
    
    template<typename F>
    auto x(F&& f) -> assume_signature<F(const std::string&)>
    {
      std::string s;
      f(s);
    }
    --
    Не можешь достичь желаемого — пожелай достигнутого.
    Отредактировано 26.07.2018 15:50 rg45 . Предыдущая версия .
    Re[2]: Указать тип параметра у функтора
    От: _NN_ www.nemerleweb.com
    Дата: 26.07.18 16:40
    Оценка:
    Проблема тут в том, что это нужно написать , тестиповать , потом поддерживать и наконец расписать комментариями как эта магия работает
    Неясно почему нет из коробки и почему нет встроенного синтаксиса как в других языках программирования .
    http://rsdn.nemerleweb.com
    http://nemerleweb.com
    Re: Указать тип параметра у функтора
    От: watchmaker  
    Дата: 26.07.18 17:05
    Оценка: 10 (1)
    Здравствуйте, _NN_, Вы писали:

    _NN>Я хотел бы как-то в сигнатуре "x" сказать, что мне нужен любой функтор принимающий std::string.

    _NN>Возможно уже есть предложения в стандарт ?
    Можно (будет) выразить концептами:
    template<typename F>
    void x(F f) requires std::is_invocable_v<F, std::string>
    {
       …
    }



    _NN>void x(f: (std::string const&) -> void)

    Замечу, что такой синтаксис плох тем, что решает уж больно частную задачу. Например, не всегда важно какое значение возвращает функция (не страшно, что там может быть не void). Или не всегда важно чтобы функция принимала именно std::string, а не любой неявно приводимый к нему тип. А иногда наоборот — нужно совсем жестко ограничить тип и запретить любый преобразования.
    Или, например, может потребоваться ограничить более одного аргумента. Скажем, часто требуется передать функцию, которая принимает два параметра любого, но одинакового типа (подобно последнему аргументу в std::accumulate).
    Как расширить синтаксис этой конструкции на все эти случаи, не сделав его при этом совсем уж ужасным, — совершенно не ясно.
    Так что таких конструкций в языке ждать не стоит. Тем более, что концепты уже предложили альтернативу.
    Отредактировано 26.07.2018 17:39 watchmaker . Предыдущая версия . Еще …
    Отредактировано 26.07.2018 17:38 watchmaker . Предыдущая версия .
    Отредактировано 26.07.2018 17:09 watchmaker . Предыдущая версия .
    Re: Указать тип параметра у функтора
    От: B0FEE664  
    Дата: 27.07.18 09:44
    Оценка:
    Здравствуйте, _NN_, Вы писали:

    _NN>Возможно уже есть предложения в стандарт ?

    Есть что-то похожее:
    template <class T>
    using A = decltype([](std::string const&){ });
    И каждый день — без права на ошибку...
    Re[2]: Указать тип параметра у функтора
    От: _NN_ www.nemerleweb.com
    Дата: 30.07.18 09:19
    Оценка:
    Здравствуйте, B0FEE664, Вы писали:

    BFE>Здравствуйте, _NN_, Вы писали:


    _NN>>Возможно уже есть предложения в стандарт ?

    BFE>Есть что-то похожее:
    BFE>
    BFE>template <class T>
    BFE>using A = decltype([](std::string const&){ }); 
    BFE>


    Так лямбду же нельзя класть в decltype .
    http://rsdn.nemerleweb.com
    http://nemerleweb.com
    Re: Указать тип параметра у функтора
    От: _NN_ www.nemerleweb.com
    Дата: 30.07.18 09:45
    Оценка: 1 (1) +1
    Здравствуйте, _NN_, Вы писали:

    Собрав всё воедино получился такой помощник:

    template <typename T, typename... Args>
    using RequiredInvocation = std::enable_if_t<std::is_invocable_v<T, Args...>>;
    
    template<typename F>
    auto void useF(F f) -> RequiresInvocation<F, std::string const&>
    {
      std::string s;
      f(s);
    }


    Не идеал, но лучше чем простыня ошибок.
    http://rsdn.nemerleweb.com
    http://nemerleweb.com
    Re[3]: Указать тип параметра у функтора
    От: B0FEE664  
    Дата: 30.07.18 18:27
    Оценка:
    Здравствуйте, _NN_, Вы писали:

    _NN>>>Возможно уже есть предложения в стандарт ?

    BFE>>Есть что-то похожее:
    BFE>>
    BFE>>template <class T>
    BFE>>using A = decltype([](std::string const&){ }); 
    BFE>>


    _NN>Так лямбду же нельзя класть в decltype .


    Это пока. А since C++20 будет можно. Спрашивали же про предложения в стандарт.
    И каждый день — без права на ошибку...
    Re[4]: Указать тип параметра у функтора
    От: watchmaker  
    Дата: 30.07.18 19:29
    Оценка:
    Здравствуйте, B0FEE664, Вы писали:


    _NN>>Так лямбду же нельзя класть в decltype .

    BFE>Это пока. А since C++20 будет можно.
    Да, с C++20 лямбды похоже будут разрешены в unevaluated context. Вот только как это поможет автору проблемы наложить ограничения на параметры? Правильно — никак
    Re[5]: Указать тип параметра у функтора
    От: B0FEE664  
    Дата: 31.07.18 08:20
    Оценка:
    Здравствуйте, watchmaker, Вы писали:

    _NN>>>Так лямбду же нельзя класть в decltype .

    BFE>>Это пока. А since C++20 будет можно.
    W>Да, с C++20 лямбды похоже будут разрешены в unevaluated context. Вот только как это поможет автору проблемы наложить ограничения на параметры? Правильно — никак

    А так нельзя?:
    template <class T>
    using OnlyStrings = decltype([](std::string const&){ });
    
    void x(OnlyStrings f)
    {
     std::string s;
     F(s);
    }
    И каждый день — без права на ошибку...
    Re[6]: Указать тип параметра у функтора
    От: watchmaker  
    Дата: 31.07.18 11:28
    Оценка:
    Здравствуйте, B0FEE664, Вы писали:


    BFE>А так нельзя?:


    Это не будет работать по многим причинам.
    Cоветую прочитать P0315R4 — там помимо простого "разрешить лямбды в unevaluated context" написано и про то, как это будет взаимодействовать со всем остальным языком. И вообще, зачем это надо и что можно делать.

    BFE>template <class T> using OnlyStrings = decltype([](std::string const&){ });
    BFE>void x(OnlyStrings f)

    Для начала, в первой строке присуствует шаблонный тип T. Во второй строке же написано просто OnlyStrings. Вопрос, куда пропало T? Тип-то OnlyStrings<something>. И как его выводить?


    Далее, какую сигнатуру имеет функция x? Можно сразу рассмотреть с позиции linker'а. Что он увидет для такой функции? Спойлер: в P0315 — это описано как "nightmare" (но это мелочь и обходится, если x сделать internal linkage).


    Ну и главное даже не это, а то, что каждая лямбда порождает свой уникальный тип. То есть аргументом функции может быть только именно та же самая лямбда, которая была внутри decltype. Не другая лямбда с такой же сингнатурой, и тем более не другое замыкание или функция, которую можно вызвать с аргументом const std::string&. Подходит только оригинальная лямбда — та самая, у которой пустое тело. (а если бы в C++20 не добавили default конструктор к лямбдам без захвата, то функцию x вообще бы было невозмножно вызвать, так как единственный объект подходящего типа присутствовал только внутри decltype).
    Отредактировано 31.07.2018 11:52 watchmaker . Предыдущая версия . Еще …
    Отредактировано 31.07.2018 11:46 watchmaker . Предыдущая версия .
    Отредактировано 31.07.2018 11:37 watchmaker . Предыдущая версия .
    Re[7]: Указать тип параметра у функтора
    От: B0FEE664  
    Дата: 31.07.18 11:49
    Оценка:
    Здравствуйте, watchmaker, Вы писали:

    W>Ну и главное даже не это, а то, что каждая лямбда порождает свой уникальный тип. То есть аргументом функции может быть только именно та же самая лямбда, которая была внутри decltype. Не другая лямбда с такой же сингнатурой, и тем более не другое замыкание или функция, которую можно вызвать с аргументом const std::string&. Подходит только оригинальная лямбда — та самая, у которой пустое тело.


    Не, ну так не интересно. Я думал будет выдан "обобщённый" тип на callable сигнатуру или хотя бы тип подходящий для спектра лямбда функций с одинаковыми параметрами...
    И каждый день — без права на ошибку...
    Re[8]: Указать тип параметра у функтора
    От: watchmaker  
    Дата: 31.07.18 11:54
    Оценка:
    Здравствуйте, B0FEE664, Вы писали:

    BFE>Не, ну так не интересно. Я думал будет выдан "обобщённый" тип на callable сигнатуру или хотя бы тип подходящий для спектра лямбда функций с одинаковыми параметрами...


    Кажется, std::function<R(T)> уже придумали :)
    Re[9]: Указать тип параметра у функтора
    От: B0FEE664  
    Дата: 31.07.18 12:16
    Оценка:
    Здравствуйте, watchmaker, Вы писали:

    BFE>>Не, ну так не интересно. Я думал будет выдан "обобщённый" тип на callable сигнатуру или хотя бы тип подходящий для спектра лямбда функций с одинаковыми параметрами...

    W>Кажется, std::function<R(T)> уже придумали

    Ну да. Вот если decltype от лябды будет выдавать тип std::function<R(T)>, то какие будут проблемы окромя производительности?
    И каждый день — без права на ошибку...
    Re[10]: Указать тип параметра у функтора
    От: watchmaker  
    Дата: 31.07.18 16:01
    Оценка:
    Здравствуйте, B0FEE664, Вы писали:

    BFE>Здравствуйте, watchmaker, Вы писали:


    W>>Кажется, std::function<R(T)> уже придумали :)


    BFE>Ну да. Вот если decltype от лябды будет выдавать тип std::function<R(T)>, то какие будут проблемы окромя производительности?


    Э... я выше про std::function<R(T)> пошутил, если что. Надеюсь ты тоже не всерьёз.
    Re[11]: Указать тип параметра у функтора
    От: B0FEE664  
    Дата: 01.08.18 08:16
    Оценка:
    Здравствуйте, watchmaker, Вы писали:

    W>>>Кажется, std::function<R(T)> уже придумали

    BFE>>Ну да. Вот если decltype от лябды будет выдавать тип std::function<R(T)>, то какие будут проблемы окромя производительности?
    W>Э... я выше про std::function<R(T)> пошутил, если что. Надеюсь ты тоже не всерьёз.

    Почти. Я не вижу причин, почему один тип лямбды не может приводится к другому типу лямбды, если у них одинаковая сигнатура.
    И каждый день — без права на ошибку...
     
    Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.