Оператор конвейера в c ++

Я написал простой оператор конвейера на C ++. Я просто хотел убедиться, что мой код был надежным, современным кодом на C ++ и правильно использовал идеальную пересылку.

Вот код:

#include <concepts>
#include <type_traits>
#include <utility>
#include <vector>
#include <iostream>
#include <deque>

//Accept an object on the left hand side and a function on the right
template <typename Any, typename... Args>
inline auto operator| (const Any& obj, std::invocable<Any> auto func){
    // Correct use of perfect forwarding??
    return func(std::forward<const Any&>(obj));
}

//Version for a functor which accepts a reference. Do I need to cover other cases?
template <typename Any, typename... Args>
inline auto operator| (Any& obj, std::invocable<Any&> auto func){
    return func(std::forward<Any&>(obj));
}

А вот как его использует пользователь:

int main() {
    auto x = std::vector{1 , 2 , 3 , 4 , 5 , 6};
    // Piping example 0
    auto y = 5 | [] (int x) {return x * 2;} 
               | [] (int x) {return x * 3;}
               ;
    
    std::cout << y << 'n';
    // Piping example 1
    x | [] (std::vector<int>& vec) -> std::vector<int>& {vec.push_back(7); return vec;};

    std::cout << x[6];
    return 0;
}

И если вы хотите очистить синтаксис лямбда (я не возражаю лично) и получить более чистый, аккуратный синтаксис, вы можете сделать:

namespace piping {
    template <typename T>
    auto push_back(const T& obj) {
        return [obj] (auto& vec) -> auto& {vec.push_back(obj); return vec;};;
    }
}

/* ... Some code */

int main() {
    auto x = std::vector{1 , 2 , 3 ,4 ,5 , 6};
    // A bit cleaner...
    x | piping::push_back(7);
}

Правильно ли я использовал современный c ++ и идеальную пересылку? Заранее спасибо.

1 ответ
1

Это кажется стоящей целью. Синтаксис выглядит немного неуклюжим, когда нам приходится писать такие встроенные лямбды, но вы, вероятно, разработаете библиотеку полезных фильтров (например, стандартные фильтры Ranges), которые будут выглядеть намного более естественными.

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

Что мы должны сделать, так это использовать typename Any&&. Хотя это выглядит как ссылка на rvalue, на самом деле это ссылка на пересылку потому что Any — параметр шаблона.

Any&& позволяет привязать параметр шаблона к либо lvalue или rvalue, поэтому нам нужна только одна функция:

template <typename Any>
auto operator| (Any&& obj, std::invocable<Any> auto&& func) {
    return func(std::forward<Any>(obj));
}

(Обратите внимание также на auto&& для func, так что при желании он может быть изменяемым функтором).


Написав это, я не уверен, что мне нравится operator|() чтобы изменить свой аргумент. я бы предпочел a | b покидать, оставлять a один, и нужно писать a |= b когда я намерен a быть измененным на месте.


Незначительные моменты:

  • Заголовки включены, но не нужны для шаблона:
    #include <vector>
    #include <iostream>
    #include <deque>
    
  • inline неявно для функции шаблона.
  • Нам не нужно указывать полный тип возвращаемого значения для лямбды «добавить 7» в примере — -> auto& легче.

  • Спасибо за все ваши отзывы и объяснения идеальной пересылки !! Я никогда не понимал этого до сих пор … Что касается ключевого слова «inline», компилятор (-O3), казалось, лучше оптимизировал мой код, когда там было «inline» ??

    — Сайрус

  • Я удивлен этим. Если inline полезен, затем продолжайте его использовать. Не думаю, что это может навредить.

    — Тоби Спейт

  • Стоит поискать в Интернете хорошие статьи о идеальная пересылка и пересылка ссылок. Еще один хороший поисковый запрос: универсальные ссылки, который является старым термином для этой конструкции.

    — Тоби Спейт

  • да, это меня тоже удивило. Фактически, обе версии моего кода встроили функцию, но версия с «встроенным» прямо вычислила все во время компиляции, в то время как версия без встроенного кода должна была вызвать лямбду.

    — Сайрус

  • Оу — наверное, есть смысл добавить constexpr к декларации? Хотя я не уверен на 100% — я только новичок в constexpr. IIRC, лямбда также может быть объявлена constexpr, но я сейчас действительно хожу по тонкому льду …

    — Тоби Спейт


Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *