如何隐藏 range-v3 的复杂范围类型?

Cyg*_*sX1 5 c++ c++14 range-v3 c++17

我需要一个带有使用 range-v3 库返回某种范围的方法的类。为了实现这样的类,我可以在该类的定义中正确地编写所有内容。例如:

#include <iostream>
#include <set>
#include <range/v3/view/transform.hpp>

class Alpha {
public:
  int x;
};

class Beta : public Alpha {};

class Foo {
public:
  std::set<Alpha*> s;

  auto r() { return s | ranges::v3::view::transform([](Alpha* a) { return static_cast<Beta*>(a); }) }
};
Run Code Online (Sandbox Code Playgroud)

但是,在我的真实案例中,该Foo::r函数非常复杂,我想隐藏它的实现。特别是,实现使用了一些额外的库,否则在Foo声明类时不需要包含这些库。

但是,当 的定义Foo::r与其声明分开时,必须明确指定其返回类型。decltype有一些帮助

头文件:

class Foo {
public:
  std::set<Alpha*> s;

  using RangeReturn = decltype(std::declval<std::set<Alpha*>&>() | ranges::v3::view::transform(std::function<Beta*(Alpha*)>()));
  RangeReturn r();
};
Run Code Online (Sandbox Code Playgroud)

实现,cpp文件:

#include "Foo.h"

Foo::RangeReturn Foo::r() {
    return s | ranges::v3::view::transform(std::function<Beta*(Alpha*)>{
      [](Alpha* a) { return static_cast<Beta*>(a); }
      });
}
Run Code Online (Sandbox Code Playgroud)

这可以立即完成隐藏Foo::r. 但是,返回值的类型仍然有效地“泄漏”了有关如何构造范围的信息。更糟糕的是,我现在被迫std::function在范围管道中明确使用一个对象。

但是,返回范围的用户真的需要这些信息吗?Foo::r关心的所有用户都是它是某种可迭代的。它有:

  • begin() 给范围的开头提供一个迭代器
  • end() 提供一些迭代器或哨兵
  • 迭代器可以递增,以迭代范围
  • 迭代器可以取消引用,给出某种类型TBeta*在示例中)。

用户不关心是否有转换视图,也不关心转换、过滤器和诸如此类的数量。

所以,我的问题是——有没有办法隐藏所有这些信息?我希望能够写出这样的东西:

在标题中:

class Foo {
public:
  std::set<Alpha*> s;

  Iterable<Beta*> r();
};
Run Code Online (Sandbox Code Playgroud)

在 cpp 文件中:

#include "Foo.h"

Iterable<Beta*> Foo::r() {
    return s | ranges::v3::view::transform([](Alpha* a) { return static_cast<Beta*>(a); });
}
Run Code Online (Sandbox Code Playgroud)

我可以接受这样一个事实,即生成的Iterable类型可能包含由于隐藏过程而无法内联的真实函数调用。链接时优化以后可能会也可能不会对其进行优化。

不幸的是,据我所知,这Iterable只是一个概念,而不是库中的一个单独类型。

Dan*_*ica 4

r()您正在寻找的返回类型是ranges::v3::any_view<Beta*>.

请注意,它应用了类型擦除,这意味着可能会产生一些显着的运行时性能损失。相关讨论:类型擦除视图的性能不佳

现场演示: https: //wandbox.org/permlink/JylKIHD0NaQsRXdB