Спотыкаясь на этот вопрос, я хотел бы указать всем, кто сталкивался с этим сегодня, что это возможно при относительно элегантном синтаксисе с использованием только стандартной библиотеки и нескольких вспомогательных классов благодаря decltype, auto и совершенной пересылке .
Определение этих двух классов:
template <class Arg, class ArgCall, class OuterCall>
class pipe {
private:
ArgCall argcall;
OuterCall outercall;
public:
typedef pipe<Arg, ArgCall, OuterCall> this_type;
pipe(ArgCall ac, OuterCall oc) : argcall(ac), outercall(oc) {}
auto operator()(Arg arg) -> decltype(outercall(argcall(arg))) {
return outercall(argcall(arg));
}
template <class NewCall>
pipe<Arg, this_type, NewCall> operator[](NewCall&& nc) {
return {*this, std::forward<NewCall>(nc)};
}
};
template <class Arg>
class pipe_source {
public:
typedef pipe_source<Arg> this_type;
Arg operator()(Arg arg) {
return arg;
}
template <class ArgCall, class OuterCall>
static pipe<Arg, ArgCall, OuterCall> create(ArgCall&& ac, OuterCall&& oc) {
return {std::forward<ArgCall>(ac), std::forward<OuterCall>(oc)};
}
template <class OuterCall>
pipe<Arg, this_type, OuterCall> operator[](OuterCall&& oc) {
return {*this, std::forward<OuterCall>(oc)};
}
};
Простая программа:
int f(int x) {
return x*x;
}
int g(int x) {
return x-2;
}
int h(int x) {
return x/2;
}
int main() {
auto foo = pipe_source<int>::create(f, g);
//or:
auto bar = pipe_source<int>()[g][h];
std::cout << foo(10) << std::endl;
std::cout << bar(10) << std::endl;
return 0;
}
Это дает дополнительное преимущество: если он находится в конвейере, если тип возвращаемого значения правильный, вы можете добавить еще одну функцию f в цепочку с конвейером [f].
Тогда:
$ g++ test.cpp -o test -std=c++11
$ ./test
98
4
$