I wrote a simple piping operator in c++. I just wanted to make sure that my code was robust, modern c++ code, and made correct use of perfect forwarding.
Here's the code:
#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));
}
And here is how the user uses it:
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;
}
And if you want to clean up the lambda syntax (I don't mind personally) and have a cleaner, neater syntax you can do:
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);
}
Did I make correct use of modern c++ and perfect forwarding? Thanks in advance.