我想将<algorithms>
中的一种算法应用于元素范围,这些元素包含在一个容器中,该容器由迭代器对定义,并包含在另一个容器中。为此,我需要一个状态为swap
的函数:只需指向带有元素的容器的指针,即可在两个容器中同步生成swap
。
这是我未完成的尝试:
#include <utility>
#include <algorithm>
#include <vector>
#include <list>
#include <iostream>
#include <random>
inline
std::ostream & operator << (std::ostream & out, const std::pair< int, int > & p)
{
return out << '{' << p.first << ", " << p.second << '}';
}
int main()
{
using L = std::list< std::pair< int, int > >;
using I = typename L::const_iterator;
using P = std::pair< I, I >;
using R = std::vector< P >;
L values;
R ranges;
auto l = std::cbegin(values);
for (int i = 0; i < 10; ++i) {
l = values.emplace(std::cend(values), i, 0);
auto & p = ranges.emplace_back(l, l);
for (int j = 1; j <= i; ++j) {
p.second = values.emplace(std::cend(values), i, j);
}
}
const auto swap = [&values] (P & l, P & r)
{
auto ll = std::next(l.second);
auto rr = std::next(r.second);
if (ll == r.first) {
values.splice(rr, values, l.first, ll);
} else if (rr == l.first) {
values.splice(ll, values, r.first, rr);
} else {
L temp;
temp.splice(std::cend(temp), values, l.first, ll);
values.splice(ll, values, r.first, rr);
values.splice(rr, std::move(temp));
}
std::swap(l, r);
};
for (const auto & p : values) {
std::cout << p << std::endl;
}
std::cout << "-----" << std::endl;
std::shuffle(std::begin(ranges), std::end(ranges), std::mt19937{std::random_device{}()}); // just an example, it can be any algo, say std::sort w/ custom comparator
for (const auto & p : values) {
std::cout << p << std::endl;
}
}
请确保上述代码中的swap
函数对象不能“参与重载解析”(由于多种原因,在当前上下文中为oxymoron,请不要专注于此)。
我能做的是像这样using P = struct { std::pair< I, I > p };
在命名空间范围(全局(命名或匿名,无关紧要))中定义迭代器对的标记版本,并重载自由函数{{ 1}}和上面代码中的lambda主体。另外,我当然应该将void swap(P & l, P & r);
设为全局变量。上面的代码阻碍了该方法的实用性。
是否有一种方法可以通过更通用的方式将有状态的values
函数从swap
传递到算法,然后在上面进行介绍?
我阅读了the article和draft关于Eric Niebler的自定义要点。但是他的方法暗示要修改STL。无论哪种方式,即使是他的方法不能使我从函数范围传递有状态的重载,我都认为不是吗?
答案 0 :(得分:1)
您可以仅将values
包含在范围对象中。
struct Range { I first; I second; L & list; }
void swap(Range & l, Range & r)
{
assert(std::addressof(l.list) == std::addressof(r.list));
using std::swap;
auto ll = std::next(l.second);
auto rr = std::next(r.second);
if (ll == r.first) {
l.list.splice(rr, l.list, l.first, ll);
} else if (rr == l.first) {
l.list.splice(ll, l.list, r.first, rr);
} else {
L temp;
temp.splice(std::cend(temp), l.list, l.first, ll);
l.list.splice(ll, l.list, r.first, rr);
l.list.splice(rr, std::move(temp));
}
swap(l.first, r.first);
swap(l.second, r.second);
}
答案 1 :(得分:0)
我找到了一种更好的方法(如果与基于swap
函数重载的方法相比)来实现所需的:两次通过的更改应用方法。它只提供线性开销,而不是感兴趣的算法的复杂性。
#include <utility>
#include <algorithm>
#include <vector>
#include <list>
#include <iostream>
#include <random>
#include <cassert>
inline
std::ostream & operator << (std::ostream & out, const std::pair< int, int > & p)
{
return out << '{' << p.first << ", " << p.second << '}';
}
int main()
{
using L = std::list< std::pair< int, int > >;
using I = typename L::const_iterator;
using P = std::pair< I, I >;
using R = std::vector< P >;
L values;
R ranges;
auto l = std::cbegin(values);
for (int i = 0; i < 10; ++i) {
l = values.emplace(std::cend(values), i, 0);
auto & p = ranges.emplace_back(l, l);
for (int j = 1; j <= i; ++j) {
p.second = values.emplace(std::cend(values), i, j);
}
}
for (const auto & p : values) {
std::cout << p << std::endl;
}
std::cout << "-----" << std::endl;
std::shuffle(std::begin(ranges), std::end(ranges), std::mt19937{std::random_device{}()});
l = std::cbegin(values);
for (const auto & range : ranges) {
auto r = std::next(range.second);
if (l != range.first) {
values.splice(l, values, range.first, r);
}
l = r;
}
assert(l == std::cend(values));
for (const auto & p : values) {
std::cout << p << std::endl;
}
}
认为它不适用于具有更严格的迭代器失效规则的容器。