如何隐藏 range-v3 的复杂范围类型?
How to hide the complex range type of a range-v3?
我需要一个 class 的方法 return 使用 range-v3 库的某种范围。
为了实现这样一个 class 我可以把它的一切都写在那个 class 的定义中。例如:
#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); }) }
};
然而,在我的真实案例中,Foo::r
函数非常复杂,我想隐藏它的实现。
特别是,该实现使用了一些额外的库,否则在声明 class Foo
时不需要包含这些库。
但是,当 Foo::r
的定义与其声明分开时,必须明确指定其 return 类型。 :
Header 个文件:
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();
};
实现,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); }
});
}
这会立即隐藏 Foo::r
的实际实现。
但是,return 值的类型仍然有效地 "leaks" 有关如何构建范围的信息。
更糟糕的是,我现在被迫在范围管道中显式使用 std::function
object。
但是 returned 范围内的用户真的需要所有信息吗? Foo::r
的所有用户都关心它是某种可迭代对象。它有:
begin()
给范围开始的迭代器
end()
给出一些迭代器或哨兵
- 迭代器可以递增,以遍历范围
- 迭代器可以取消引用,给出某种类型
T
(示例中的 Beta*
)。
用户不关心有没有转换视图,也不关心转换、过滤器等的数量。
那么,我的问题是 -- 有没有办法隐藏所有这些信息?我希望能够写出这样的东西:
在header:
class Foo {
public:
std::set<Alpha*> s;
Iterable<Beta*> r();
};
在cpp文件中:
#include "Foo.h"
Iterable<Beta*> Foo::r() {
return s | ranges::v3::view::transform([](Alpha* a) { return static_cast<Beta*>(a); });
}
我可以接受这样一个事实,即生成的 Iterable
类型可能包含由于隐藏过程而无法内联的真实函数调用。
link-time 优化以后可能会也可能不会对其进行优化。
不幸的是,据我所知,这样的 Iterable
只是一个概念,而不是库中的一个单独类型。
您要找的r()
的return类型是ranges::v3::any_view<Beta*>
。
请注意,它应用类型擦除,这意味着一些运行时性能损失可能很严重。相关讨论:poor performance of type-erased views.
我需要一个 class 的方法 return 使用 range-v3 库的某种范围。 为了实现这样一个 class 我可以把它的一切都写在那个 class 的定义中。例如:
#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); }) }
};
然而,在我的真实案例中,Foo::r
函数非常复杂,我想隐藏它的实现。
特别是,该实现使用了一些额外的库,否则在声明 class Foo
时不需要包含这些库。
但是,当 Foo::r
的定义与其声明分开时,必须明确指定其 return 类型。
Header 个文件:
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();
};
实现,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); }
});
}
这会立即隐藏 Foo::r
的实际实现。
但是,return 值的类型仍然有效地 "leaks" 有关如何构建范围的信息。
更糟糕的是,我现在被迫在范围管道中显式使用 std::function
object。
但是 returned 范围内的用户真的需要所有信息吗? Foo::r
的所有用户都关心它是某种可迭代对象。它有:
begin()
给范围开始的迭代器end()
给出一些迭代器或哨兵- 迭代器可以递增,以遍历范围
- 迭代器可以取消引用,给出某种类型
T
(示例中的Beta*
)。
用户不关心有没有转换视图,也不关心转换、过滤器等的数量。
那么,我的问题是 -- 有没有办法隐藏所有这些信息?我希望能够写出这样的东西:
在header:
class Foo {
public:
std::set<Alpha*> s;
Iterable<Beta*> r();
};
在cpp文件中:
#include "Foo.h"
Iterable<Beta*> Foo::r() {
return s | ranges::v3::view::transform([](Alpha* a) { return static_cast<Beta*>(a); });
}
我可以接受这样一个事实,即生成的 Iterable
类型可能包含由于隐藏过程而无法内联的真实函数调用。
link-time 优化以后可能会也可能不会对其进行优化。
不幸的是,据我所知,这样的 Iterable
只是一个概念,而不是库中的一个单独类型。
您要找的r()
的return类型是ranges::v3::any_view<Beta*>
。
请注意,它应用类型擦除,这意味着一些运行时性能损失可能很严重。相关讨论:poor performance of type-erased views.