我仔细检查了标准并同意没有任何要求 shuffle 对其使用随机数做出任何保证。它只是说:
“在此函数的实现使用随机数的范围内,g 引用的对象应作为实现的随机源。”。
所以,剩下的问题似乎是您是否对
任何特定的实现定义或观察到的行为
实施(S),或将坚持符合标准的便携式
解决方案(例如,改组代理对象或使用数组
指数)...?
根据您的 cmets,您似乎反对索引数组的建议,因此在下面 - 使用自定义迭代器和代理对向量本身进行洗牌的实现...
(做的不是很仔细——更多的是概念证明/插图,所以在使用任何重要的东西之前请仔细检查......)
该方法需要一个 move_together 对象来保留对向量的引用,然后传递 shuffle iterators,该对象具有指向 move_together 对象的指针和正在处理的向量中的索引。您可以通过前面提到 move_together 对象并在迭代器中直接使用指向两个向量的指针或引用来简化这一点。当迭代器被取消引用时,它们会返回支持swapping 的代理对象。
它表面上适用于 GCC 10.2 和 clang 10,但对于另一个需要更完整的迭代器或代理的编译器,std::shuffle 可能有不同的实现......
#include <iostream>
#include <vector>
#include <random>
#include <string>
#include <algorithm>
template <typename T1, typename T2>
struct move_together;
template <typename T1, typename T2>
struct proxy
{
const move_together<T1, T2>* p_;
const size_t i_;
proxy& operator=(const proxy& rhs);
};
template <typename T1, typename T2>
struct move_together
{
move_together(std::vector<T1>& v1, std::vector<T2>& v2)
: v1_(v1), v2_(v2)
{ }
struct iterator
{
using iterator_category = std::random_access_iterator_tag;
using difference_type = ssize_t;
using value_type = proxy<T1, T2>;
using pointer = value_type*;
using reference = value_type&;
const move_together* p_;
size_t i_;
value_type operator*() { return {p_, i_}; }
bool operator==(const iterator& rhs) const { return i_ == rhs.i_; }
bool operator!=(const iterator& rhs) const { return !(*this == rhs); }
difference_type operator-(const iterator& rhs) const
{ return i_ - rhs.i_; }
iterator operator+(int distance) const
{ return {p_, i_ + distance}; }
iterator operator++(int) { auto x = *this; ++i_; return x; }
iterator& operator++() { ++i_; return *this; }
};
iterator begin() { return {this, 0}; }
iterator end() { return {this, std::min(v1_.size(), v2_.size())}; }
std::vector<T1>& v1_;
std::vector<T2>& v2_;
};
template <typename T1, typename T2>
proxy<T1, T2>& proxy<T1, T2>::operator=(const proxy<T1, T2>& rhs)
{
p_->v1_[i_] = rhs.p_->v1_[rhs.i_];
p_->v2_[i_] = rhs.p_->v2_[rhs.i_];
}
template <typename T1, typename T2>
void swap(proxy<T1, T2> lhs, proxy<T1, T2> rhs) {
using std::swap;
swap(lhs.p_->v1_[lhs.i_], rhs.p_->v1_[rhs.i_]);
swap(lhs.p_->v2_[lhs.i_], rhs.p_->v2_[rhs.i_]);
}
int main()
{
std::vector<int> v1{ {1, 2, 3, 4, 5} };
std::vector<std::string> v2{ {"one", "two", "three", "four", "five"} };
std::random_device rd;
std::mt19937 rng{rd()};
move_together m{v1, v2};
std::shuffle(m.begin(), m.end(), rng);
for (const auto& x : v1) std::cout << x << '/';
std::cout << '\n';
for (const auto& x : v2) std::cout << x << '/';
std::cout << '\n';
}