С использованием библиотека 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?
У вас есть две неоднозначные перегрузки 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;
Последняя версия из git уже содержит action::shuffle
, Может использоваться следующим образом:
#include <random>
std::mt19937 gen;
...
v |= action::push_back(view::iota(0, N)) | action::shuffle(gen);