首页 > 解决方案 > 如何通过重载 | 链接和序列化函数 操作员

问题描述

我试图弄清楚如何一般地重载给定基类对象以序列化或链接与工作方式或工作operator|()方式相似的函数调用......我想通过管道运算符链接它们......这样我可以拥有一系列独立函数,并在单个数据对象上调用它们......换句话说,对同一数据类型执行多个转换,就像在流系统中......pipesoperator<<()

考虑以下伪代码示例:此代码可能无法编译,我没有方便的编译器,并且我可能在函数指针或函数对象作为运算符中的参数时使用了错误的语法......这只是来说明我所追求的模式和行为。

template<typename T>
typedef T(*Func)(T); // Function Pointer for functors-lambdas-etc... 

template<typename T>
struct pipe_object {
    T operator|(T(*Func)(T) func) {
        return func(T);
    }

    T operator()(T(*Func)(T) func) {
        return this->operator|(t, func);
    }
};

然后我可能想像这样使用它们:

constexpr int add_one_f(int x) {
    return (x+1);
}

constexpr int add_two_f(int x) {
   return (x+2);
}


void foo() {
    pipe_object<int> p1 = {};
    pipe_object<int> p2 = {};

    int result = p1(&add_one) | p2(&add_two); 

    // or something like...

    int result = p1 | p2; // ... etc ...

    // or something like:
    p1 = add_one | add_two | p2; // ... etc ...
}

我只是不知道如何在运算符中传播intput- ......我是否必须重载两个版本以便它可以识别?output|()|(lhs, rhs)|(rhs, lhs)

不仅如此,如果我想扩展它以便我functorslambdas将采取多个论点怎么办......

我一直在对此进行谷歌搜索,只找到了一些资源,但没有任何具体、简单、优雅且至少具有 C++17 功能的最新资源......

如果您知道有关此主题的任何好的原始资料,请告诉我!

标签: c++operator-overloadingc++17chainingpipelining

解决方案


首先我假设你有一些看起来像这样的基础知识

#include <iostream>
struct vec2 {
    double x;
    double y;
};
std::ostream& operator<<(std::ostream& stream, vec2 v2) {return stream<<v2.x<<','<<v2.y;}

//real methods
vec2 translate(vec2 in, double a) {return vec2{in.x+a, in.y+a};} //dummy placeholder implementations
vec2 rotate(vec2 in, double a) {return vec2{in.x+1, in.y-1};}
vec2 scale(vec2 in, double a) {return vec2{in.x*a, in.y*a};}

所以你想要的是一个操作的代理类,其中一个代理对象是用函数和“其他参数”构造的。(我将函数设为模板参数,这样可以防止使用函数指针,并帮助优化器内联,从而使这几乎为零开销。)

#include <type_traits>
//operation proxy class
template<class rhst, //type of the only parameter
     vec2(*f)(vec2,rhst)> //the function to call
class vec2_op1 {
    std::decay_t<rhst> rhs; //store the parameter until the call
public:
    vec2_op1(rhst rhs_) : rhs(std::forward<rhst>(rhs_)) {}
    vec2 operator()(vec2 lhs) {return f(lhs, std::forward<rhst>(rhs));}
};

//proxy methods
vec2_op1<double,translate> translate(double a) {return {a};}
vec2_op1<double,rotate> rotate(double a) {return {a};}
vec2_op1<double,scale> scale(double a) {return {a};}

然后你简单地把它变成可链接的

//lhs is a vec2, rhs is a vec2_operation to use
template<class rhst, vec2(*f)(vec2,rhst)>
vec2& operator|(vec2& lhs, vec2_op1<rhst, f>&& op) {return lhs=op(lhs);}

用法很简单:

int main() {
    vec2 v2{3,5};
    v2 | translate(2.5) | rotate(30) | translate(3) | scale(2);
    std::cout << v2;
}

http://coliru.stacked-crooked.com/a/9b58992b36ff12d3

注意:没有分配,没有指针,没有复制或移动。这应该生成与您v2.translate(2.5); v2.rotate(30); v2.scale(10); 直接生成的代码相同的代码。


推荐阅读