我有一个数学库。在该库中,我具有在单工空间中操纵超平面的功能,以便可以通过各种方式对超平面进行排序。
事实证明,这些超平面可以在不同的上下文中表示不同的事物。虽然数学是相同的,但是在每种情况下,超平面意味着不同的事物,并且与不同的数据结构相关联。
能够编写一次操纵超平面的代码但允许它们处理不同的数据结构对我来说是有利的。
下面是一个简化的示例,试图解释我要做什么:
// Assume this struct represent my hyperplane, or whatever
// construct I want to be able to manipulate.
struct Math {
int x;
};
// Here is my function `foo` which expects a range of Math. It does
// some work on it, and re-arranges it as it is useful to me.
template <typename It>
void foo(It begin, It end) {
while (begin < end) {
--end;
if (begin->x < end->x)
std::iter_swap(begin, end);
++begin;
}
}
template <typename Range>
void foo(Range & r) {
foo(ranges::begin(r), ranges::end(r));
}
这基本上是我的基础功能,对于使用我的超平面的每个其他类(或在本例中为Math
类)来说都是通用的。
现在在我的图书馆的其他地方,我有一些看起来像这样的类:
struct Something {
int additional_metadata;
Math contextual_name_for_math;
};
struct SomethingElse {
double other_metadata;
std::vector<int> some_other_metadata;
Math another_different_contextual_name;
};
现在,我需要能够将foo
应用于这些类的范围,并根据它们包含的Math
的属性来重新排列它们。同时:
foo
不知道Math
在每个类中的上下文名称。foo
不在乎存在的其他元数据。我想写的是这样的
// Create data
std::vector<Something> S{{1,{2}},{3,{4}},{5,{6}}};
// Transform data in to view of Math, so that 'foo' can work on it
auto S_as_math = S | ranges::view::transform(
// I guess I can remove the consts here, although `foo` does not
// really need to alter the values, it only shuffles things around.
[](auto const& s) -> Math const& { return s.contextual_name_for_math; }
);
// Do work inline on the view, but hopefully on the underlying S too.
foo(S_as_math);
// Print results.
for (auto s : S) {
std::cout << '(' << s.additional_metadata << ", "
<< s.contextual_name_for_math.x << ")\n";
}
std::cout << "\n";
// expected, keeps Math and associated metadata together:
//
// (5, 6)
// (3, 4)
// (1, 2)
//
// NOT WANTED, only shuffles Math with no regard for metadata:
//
// (1, 6)
// (3, 4)
// (5, 2)
目前,我正在通过向boost::transform_iterator
传递foo
到Math
组件来提取iter_swap
组件,并通过在{{1 }}能够知道它是否正在传递给代理迭代器,并且总是交换基础原件。这实现了我想要的。
我很好奇是否可以使用foo
来做到这一点。目前,如果我删除了用于解开ranges-v3
类的lambda中的const
,那么我就可以编译此示例,但是Something
仅对foo
进行洗牌,而不会将它们与元数据放在一起。
答案 0 :(得分:0)
将您的转换函数传递给foo
,不要foo
转换范围。
template <typename It, typename UnaryFunction>
void foo(It begin, It end, UnaryFunction func) {
while (begin < end) {
--end;
if (func(*begin).x < func(*end).x)
std::iter_swap(begin, end);
++begin;
}
}
template <typename Range, typename UnaryFunction>
void foo(Range & r, UnaryFunction func) {
foo(ranges::begin(r), ranges::end(r));
}
int main()
{
std::vector<Something> S{{1,{2}},{3,{4}},{5,{6}}};
auto S_as_math = [](auto const& s) { return s.contextual_name_for_math; };
foo(S, S_as_math);
for (auto s : S) {
std::cout << '(' << s.additional_metadata << ", "
<< s.contextual_name_for_math.x << ")\n";
}
std::cout << "\n";
}
如果您仅使用UnaryFunction
范围,则可以将原始模板或默认Math
保留为identity function。