如何为 random_shuffle 编写 range-v3 操作?
How to write a range-v3 action for random_shuffle?
使用 range-v3 library(@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;
}
}
/// @}
}
}
Live Example 无法编译,因为未找到某些 operator()
深深隐藏在某处的东西。
据我所知,我忠实地从类似代码中翻译了上面的代码,例如action::sort()
。唯一的区别是 random_shuffle()
有两个重载(一个采用随机生成器),而所有其他操作(包括 sort
)都有一个重载,其额外参数(比较器,谓词、投影仪等)。这转化为上面 random_shuffle_fn
的两个 bind()
静态成员函数,而所有其他操作只有一个 bind()
重载。
问题:如何为 random_shuffle 编写 range-v3 动作?
您有两个不明确的 random_shuffle_function::operator()(Rng&&)
重载,您的 "error catching" 重载需要被限制为仅接受正确重载拒绝的那些参数(我们 确实 需要 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);
使用 range-v3 library(@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;
}
}
/// @}
}
}
Live Example 无法编译,因为未找到某些 operator()
深深隐藏在某处的东西。
据我所知,我忠实地从类似代码中翻译了上面的代码,例如action::sort()
。唯一的区别是 random_shuffle()
有两个重载(一个采用随机生成器),而所有其他操作(包括 sort
)都有一个重载,其额外参数(比较器,谓词、投影仪等)。这转化为上面 random_shuffle_fn
的两个 bind()
静态成员函数,而所有其他操作只有一个 bind()
重载。
问题:如何为 random_shuffle 编写 range-v3 动作?
您有两个不明确的 random_shuffle_function::operator()(Rng&&)
重载,您的 "error catching" 重载需要被限制为仅接受正确重载拒绝的那些参数(我们 确实 需要 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);