Как написать действие range-v3 для random_shuffle?

С использованием библиотека range-v3 (@EricNiebler), делает написание алгоритмического кода намного более компактным, например, вот как сгенерировать кучу случайных чисел:

#include <range/v3/all.hpp>
#include <iostream>
#include <vector>

int main()
{
using namespace ranges;

auto const N = 10;
std::vector<int> v;
v.reserve(N);

v |= action::push_back(view::iota(0, N));
random_shuffle(v);
copy(v, ostream_iterator<>(std::cout, ","));
}

Живой пример.

Тем не менее, я бы предпочел расширить трубопровод с гипотетической action::random_shuffle() как это

v |= action::push_back(view::iota(0, N)) | action::random_shuffle();

Вот моя попытка написать такое действие (к сожалению, написание нового кода range-v3 немного более многословно, чем использование библиотеки)

#include <functional> // bind, placeholders::_1

namespace ranges
{
inline namespace v3
{
/// \addtogroup group-actions
/// @{
namespace action
{
struct random_shuffle_fn
{
private:
friend action_access;

static auto bind(random_shuffle_fn random_shuffle)
RANGES_DECLTYPE_AUTO_RETURN
(
std::bind(random_shuffle, std::placeholders::_1)
)

template<typename Gen>
static auto bind(random_shuffle_fn random_shuffle, Gen && rand)
RANGES_DECLTYPE_AUTO_RETURN
(
std::bind(random_shuffle, std::placeholders::_1, bind_forward<Gen>(rand))
)
public:
struct ConceptImpl
{
template<typename Rng,
typename I = range_iterator_t<Rng>>
auto requires_(Rng&&) -> decltype(
concepts::valid_expr(
concepts::model_of<concepts::RandomAccessRange, Rng>(),
concepts::is_true(Permutable<I>())
));
};

template<typename Rng>
using Concept = concepts::models<ConceptImpl, Rng>;

template<typename Rng,
CONCEPT_REQUIRES_(Concept<Rng>())>
Rng operator()(Rng && rng) const
{
ranges::random_shuffle(rng);
return std::forward<Rng>(rng);
}

template<typename Rng, typename Gen,
CONCEPT_REQUIRES_(Concept<Rng>())>
Rng operator()(Rng && rng, Gen && rand) const
{
ranges::random_shuffle(rng, std::forward<Gen>(rand));
return std::forward<Rng>(rng);
}

#ifndef RANGES_DOXYGEN_INVOKED
template<typename Rng>
void operator()(Rng &&) const
{
CONCEPT_ASSERT_MSG(RandomAccessRange<Rng>(),
"The object on which action::random_shuffle operates must be a model of the ""RandomAccessRange concept.");
using I = range_iterator_t<Rng>;
CONCEPT_ASSERT_MSG(Permutable<I>(),
"The iterator type of the range passed to action::random_shuffle must allow its ""elements to be permuted; that is, the values must be movable and the ""iterator must be mutable.");
}
#endif
};

/// \ingroup group-actions
/// \relates sort_fn
/// \sa `action`
namespace
{
constexpr auto&& random_shuffle = static_const<action<random_shuffle_fn>>::value;
}
}
/// @}
}
}

Живой пример который не компилируется, потому что некоторые operator() глубоко спрятанный где-то не найден.

Насколько я вижу, я добросовестно перевел приведенный выше код из аналогичного кода, например, action::sort(), Разница лишь в том, что random_shuffle() имеет две перегрузки (одна принимает случайный генератор), тогда как все остальные действия (включая sortвсе они имеют одну перегрузку со значениями по умолчанию для своих дополнительных параметров (компараторы, предикаты, проекторы и т. д.). Это переводит на два bind() статические функции-члены random_shuffle_fn выше, тогда как все другие действия имеют только один bind() перегрузки.

Вопрос: как написать действие range-v3 для random_shuffle?

7

Решение

У вас есть две неоднозначные перегрузки random_shuffle_function::operator()(Rng&&)Ваша перегрузка «ловить ошибки» должна быть ограничена, чтобы принимать только те аргументы, которые надлежащая перегрузка отклоняет (мы действительно нужны концепции C ++, поэтому мне больше никогда не придется ограничивать перегрузки SFINAE):

#ifndef RANGES_DOXYGEN_INVOKED
template<typename Rng,
CONCEPT_REQUIRES_(!Concept<Rng>())>
void operator()(Rng &&) const
{
CONCEPT_ASSERT_MSG(RandomAccessRange<Rng>(),
"The object on which action::random_shuffle operates must be a model of the ""RandomAccessRange concept.");
using I = range_iterator_t<Rng>;
CONCEPT_ASSERT_MSG(Permutable<I>(),
"The iterator type of the range passed to action::random_shuffle must allow its ""elements to be permuted; that is, the values must be movable and the ""iterator must be mutable.");
}
#endif

Кроме того, вам нужно пропустить через action::random_shuffle:

v |= action::push_back(view::iota(0, N)) | action::random_shuffle;

DEMO

3

Другие решения

Последняя версия из git уже содержит action::shuffle, Может использоваться следующим образом:

#include <random>
std::mt19937 gen;
...
v |= action::push_back(view::iota(0, N)) | action::shuffle(gen);
3

По вопросам рекламы [email protected]