11

我开始使用 Boost::Range 以便在 C++ 中拥有一个惰性转换管道。我现在的问题是如何将管道分成更小的部分。假设我有:

int main(){
  auto map = boost::adaptors::transformed; // shorten the name
  auto sink = generate(1) | map([](int x){ return 2*x; })
                          | map([](int x){ return x+1; })
                          | map([](int x){ return 3*x; });
  for(auto i : sink)
    std::cout << i << "\n";
}

我想用 a 替换前两张地图magic_transform,即:

int main(){
  auto map = boost::adaptors::transformed; // shorten the name
  auto sink = generate(1) | magic_transform()
                          | map([](int x){ return 3*x; });
  for(auto i : sink)
    std::cout << i << "\n";
}

一个人会怎么写magic_transform?我查阅了Boost::Range 的文档,但我无法很好地掌握它。

附录:我正在寻找这样的类:

class magic_transform {
    ... run_pipeline(... input) {
        return input | map([](int x){ return 2*x; })
                     | map([](int x){ return x+1; });
};
4

2 回答 2

6

最困难的问题是找出代码中的返回类型。decltype和 lambdas 不能很好地混合(见这里),所以我们必须考虑另一种方法:

auto map = boost::adaptors::transformed;

namespace magic_transform
{
   std::function<int(int)> f1 = [](int x){ return 2*x; };
   std::function<int(int)> f2 = [](int x){ return x+1; };
   template <typename Range>
   auto run_pipeline(Range input) -> decltype(input | map(f1) | map(f1))
   {
        return input | map(f1) | map(f2);
   }
}

...
auto sink = magic_transform::run_pipeline(generate(1))
                          | map([](int x){ return 3*x; });

简单的解决方案是将 lambda 插入其中std::function,以便我们可以使用它decltype来推断返回类型。我magic_transform在示例中使用了命名空间,但如果您愿意,也可以将此代码改编为一个类。这是使您的代码适应上述内容的链接。

此外,std::function在这里使用可能有点过分了。相反,您可以只声明两个普通函数(示例)。

我也在试验boost::any_range,似乎与 C+11 lambdas 等有一些不兼容。我能得到的最接近的是以下(示例):

auto map = boost::adaptors::transformed;
using range = boost::any_range<
               const int,
               boost::forward_traversal_tag,
               const int&,
               std::ptrdiff_t
               >;

namespace magic_transform
{
    template <typename Range>
    range run_pipeline(Range r)
    {
        return r | map(std::function<int(int)>([](int x){ return 2*x; }))
             | map(std::function<int(int)>([](int x){ return x+1; }));
    }
}

int main(){
  auto sink = magic_transform::run_pipeline(boost::irange(0, 10))
                          | map([](int x){ return 3*x; });
  for(auto i : sink)
    std::cout << i << "\n";
}
于 2012-11-06T03:02:48.557 回答
1

我认为会起作用:

auto magic_transform()->decltype(boost::adaptors::transformed(std::function<int(int)>())
{
    std::function<int(int)> retval = [](int x){ return [](int x){ return x+1; }(2*x);
    return boost::adaptors::transformed(retval);
}

但这可能不是您想要的。:) (上面代码中的笑话:2*x+1 的链式 lambda,在基本上实现上使用 decltype 来查找返回类型),

查看 的源代码http://www.boost.org/doc/libs/1_46_1/boost/range/adaptor/transformed.hppmagic_transform想要返回的类型是boost::range_detail::transform_holder<T>,其中 T 是函数的类型。

当您使用 lambda 在堆栈上执行此操作时,T最终会成为一些非常狭窄的类型。如果您想在不暴露所有细节的情况下传递抽象转换,那么使用std::function<outtype(intype)>可能是合理的(运行时开销会很小)。

希望有效。

于 2012-11-05T23:35:39.280 回答