重载按位OR('|')对于链接操作无法按预期进行

时间:2019-07-28 11:34:08

标签: c++ operator-overloading c++17

我正在尝试按位或'|'重载操作符,以便我可以将不同的操作一个接一个地链接。

我实现了以下代码。

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

struct Test
{
    explicit Test(vector<int> vdata) : data_(vdata) { }

    vector<int>& operator()()
    {
        return data_;
    }

    template<typename Pred>
    Test operator | (Pred P)
    {
        *this = P;
        return *this;
    }

    vector<int> data_;
};

template <typename Con, typename Pred>
Con Transform(Con& C, Pred P)
{
    vector<int> res;

    transform(begin(C()), end(C()),back_inserter(res),  P);

    return Con(res);
}

template <typename Con, typename Pred>
Con Filter(Con& C, Pred P)
{
    vector<int> res;

    remove_copy_if(begin(C()), end(C()), back_inserter(res), P);

    return Con(res);
}

int main()
{
    vector<int> vdata{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};

    auto DoubleIt = [](int& v) {
        return v *= 2;
    };

    auto Remove_Lteq4 = [](auto v) {
        return v <= 4;
    };

    auto Remove_divideby3=[](auto v) 
    {
        return !(v % 3);
    };

    Test test(vdata);

    Test test1 =  
     test | Filter(test, Remove_Lteq4) |  
     Transform(test, DoubleIt) | 
     Filter(test, Remove_divideby3);

    // output
    for (auto v : test1())
        cout << v << " ";            
}

代码运行良好,可以在Cxxdroid 2.0_arm离线编译器C ++ 14 / C ++ 17上提供预期的输出,但是我在在线编译器上得到了不同的结果

输入: 向量= {1,2,3,4,5,6,7,8,9,10,11,12} 依次应用的操作是 过滤器值<= 4,然后将剩余值乘以2,然后将过滤器值除以3

结果离线编译器(Cxxdroid) 预期输出:10、14、16、20、22 实际输出:10、14、16、20、22

在线编译器 实际输出:1,2,4,5,7,8,10,11(如果仅对输入应用remove_divideby3函数,则实际上是输出)。

我正在努力解决它一段时间,无法弄清输出不同的原因。有人可以告诉我我在哪里犯错。 如果我一次只应用一种操作,则代码可以正常工作。

如果有人可以回答其他查询,我也将非常感激。

  1. 我是否需要编写副本构造函数/副本分配,移动构造函数和移动分配。

  2. 在按值和按引用传递和返回对象方面,我对对象的用法是否正确?我认为问题出在这里,可能在需要引用的地方传递对象。

  3. 是否需要重载'|'在全球范围内。

注意:使用命名空间std;仅出于方便的目的,因为我在手机上键入了代码。

谢谢

2 个答案:

答案 0 :(得分:1)

您正在执行此操作(我重新排列了管道以使其更易于阅读):

Test test1 =  
 test | Filter(test, Remove_Lteq4)
      | Transform(test, DoubleIt)
      | Filter(test, Remove_divideby3);

让我们从后面开始。 Filter(test, Remove_divideby3)是做什么的?它返回一个容器,该容器包含test中满足Remove_divideby3的所有元素。但是test是您的开始,它不依赖于此操作的任何部分。另一个FilterTransform返回什么都无关紧要,因为这些值都不用作此Filter的输入。

这就是为什么您得到的与写的没有什么不同的原因:

Test test1 = Filter(test, Remove_divideby3);

您的适配器 都将范围作为参数:

Filter(Transform(Filter(test, Remove_Lteq4), DoubleIt), Remove_divideby3)

使用管道:

test | Filter(Remove_Lteq4) | Transform(DoubleIt) | Filter(Remove_divideby3)

不能同时使用。

答案 1 :(得分:1)

template<class F>
struct pipe_func {
  F f;

  template<class Lhs>
  friend auto operator|( Lhs&& lhs, pipe_func rhs ) {
    return rhs.f( std::forward<Lhs>(lhs) );
  }
};
template<class F>
pipe_func(F)->pipe_func<F>;

有一个构建基块。 pipe_target接受一个函数,当通过管道传输的东西将其馈送到该函数并返回结果时。

现在假设您要编写过滤器。

template<class Pred>
auto Filter( Pred p ) {
  return pipe_func{[p]( auto&& container ) {
    using std::begin; using std::end;
    using R = std::decay_t< decltype(container) >;
    R retval;
    std::remove_copy_if( begin(container), end(container), std::back_inserter(retval), p );
    return retval;
  }};
}

所以filter是一个带有谓词的函数。

它返回一个pipe_funcpipe_func中有一个lambda来应用谓词。

转换类似:

template<class Pred>
auto Transform( Pred p ) {
  return pipe_func{[p]( auto&& container ) {
    using std::begin; using std::end;
    using R = std::decay_t< decltype(container) >;
    R retval;
    std::transform( begin(container), end(container), std::back_inserter(retval), p );
    return retval;
  }};
}

测试代码如下:

std::vector<int> test1 =  
 vdata | Filter(Remove_Lteq4) |  
 Transform(DoubleIt) | 
 Filter(Remove_divideby3);

Live example

现在,我们可以将带有两个参数的函数传递给函数的解决方案有点麻烦;该函数仍然认为它需要2个参数,因此将第二个参数传递给它很棘手。

我会避免的。任何解决方案都是骇客。