是否有更惯用的方法来使用通过模板传递的标志来专门化行为?
Is there a more idiomatic way to specialise behaviour using flags passed via template?
对于模棱两可的标题表示歉意。
这是我的代码:
struct LowHigh
{
};
struct HighLow
{
};
template < class LookupScheme>
struct ladder_base
{
using value_type = price_depth;
using ladder_type = std::vector< value_type >;
template < class T >
struct lookup;
template <>
struct lookup< LowHigh >
{
static constexpr auto func = std::upper_bound< ladder_type::iterator, value_type >;
};
template <>
struct lookup< HighLow >
{
static constexpr auto func = std::lower_bound< ladder_type::iterator, value_type >;
};
void
insert(value_type v)
{
auto iter = lookup< LookupScheme >::func(std::begin(data_), std::end(data_), v);
data_.insert(iter, std::move(v));
}
protected:
std::vector< value_type > data_;
};
} // namespace detail
struct bid_ladder : detail::ladder_base< detail::HighLow >
{
};
struct offer_ladder : detail::ladder_base< detail::LowHigh >
{
};
我专注于 lookup::func
,具体取决于作为模板类型传递的方案。目前只有两种可能的方案:LowHigh
& HighLow
。这具有确定基础向量如何排序的效果。
有没有更idiomatic/cleaner的方式来表达这个逻辑?
做这种事情的惯用方法是让模板参数包含直接调用的代码,而不是像您正在做的那样通过标记类型间接调用。
请注意,在那个时候,您总是可以直接将 std::upper_bound
作为模板参数传递。
此外,由于它被标记为 c++20
,您最好使用一个概念来限制可以传递给 ladder_base
的类型。
#include <concepts>
#include <vector>
using price_depth = int;
template<typename T>
concept LookupScheme = requires (const T& x, const std::vector<price_depth>& v) {
{x(v.begin(), v.end(), price_depth{})} -> std::same_as<decltype(v.begin())>;
};
namespace detail {
struct LowHigh {
template<typename ForwardIt, typename T>
decltype(auto) operator()(ForwardIt first, ForwardIt last, const T& value ) const {
return std::upper_bound(first, last, value);
}
};
struct HighLow {
template<typename ForwardIt, typename T>
decltype(auto) operator()(ForwardIt first, ForwardIt last, const T& value ) const {
return std::lower_bound(first, last, value);
}
};
template <LookupScheme Scheme>
struct ladder_base
{
using value_type = price_depth;
using ladder_type = std::vector< value_type >;
void insert(value_type v)
{
auto iter = Scheme::exec(std::begin(data_), std::end(data_), v);
data_.insert(iter, std::move(v));
}
protected:
std::vector< value_type > data_;
};
} // namespace detail
struct bid_ladder : detail::ladder_base< detail::LowHigh >
{
};
struct offer_ladder : detail::ladder_base< detail::HighLow >
{
};
您可以在标准库的排序容器中看到相同的方法,例如 std::map<>
的 Compare
参数。
template<class C>
auto lookup(C& c, value_type v) {
if constexpr(std::same_as<LookupScheme, LowHigh>)
return std::upper_bound(c.begin(), c.end(), v);
else if constexpr(std::same_as<LookupScheme, HighLow>)
return std::lower_bound(c.begin(), c.end(), v);
}
void insert(value_type v) {
auto iter = lookup(data_, v);
data_.insert(iter, std::move(v));
}
这些算法将比较对象作为它们的最后一个参数 - 因此您可以利用它来发挥自己的优势。
template < class Compare >
struct ladder_base
{
using value_type = price_depth;
using ladder_type = std::vector< value_type >;
void
insert(value_type v)
{
auto iter = std::upper_bound(data_.begin(), data_.end(), v, Compare{} );
data_.insert(iter, std::move(v));
}
protected:
std::vector< value_type > data_;
};
然后使用 ladder_base<std::less<>>
或 ladder_base<std::greater<>>
,具体取决于您想要的排序顺序。
请注意 std::lower_bound
和 std::upper_bound
不是反义词 ,因此您的原文并不完全正确。 lower_bound
为您提供第一个元素 >= x
,upper_bound
为您提供第一个元素 > x
。因此,从一个更改为另一个不会更改您的排序顺序(两者都需要递增顺序),只有比较对象会影响它。
例如:
std::vector<int> v = {1, 3, 5, 7};
auto i = std::lower_bound(v.begin(), v.end(), 3); // this is the 3
auto j = std::upper_bound(v.begin(), v.end(), 3); // this is the 5
请注意,向量是按递增顺序排序的,但两次调用都是完美的 well-formed。如果您想要反向排序,则必须将 std::greater{}
作为比较对象传入(如我所示)。
但无论哪种方式,您都希望使用 std::upper_bound
- 无论排序顺序如何。
对于模棱两可的标题表示歉意。
这是我的代码:
struct LowHigh
{
};
struct HighLow
{
};
template < class LookupScheme>
struct ladder_base
{
using value_type = price_depth;
using ladder_type = std::vector< value_type >;
template < class T >
struct lookup;
template <>
struct lookup< LowHigh >
{
static constexpr auto func = std::upper_bound< ladder_type::iterator, value_type >;
};
template <>
struct lookup< HighLow >
{
static constexpr auto func = std::lower_bound< ladder_type::iterator, value_type >;
};
void
insert(value_type v)
{
auto iter = lookup< LookupScheme >::func(std::begin(data_), std::end(data_), v);
data_.insert(iter, std::move(v));
}
protected:
std::vector< value_type > data_;
};
} // namespace detail
struct bid_ladder : detail::ladder_base< detail::HighLow >
{
};
struct offer_ladder : detail::ladder_base< detail::LowHigh >
{
};
我专注于 lookup::func
,具体取决于作为模板类型传递的方案。目前只有两种可能的方案:LowHigh
& HighLow
。这具有确定基础向量如何排序的效果。
有没有更idiomatic/cleaner的方式来表达这个逻辑?
做这种事情的惯用方法是让模板参数包含直接调用的代码,而不是像您正在做的那样通过标记类型间接调用。
请注意,在那个时候,您总是可以直接将 std::upper_bound
作为模板参数传递。
此外,由于它被标记为 c++20
,您最好使用一个概念来限制可以传递给 ladder_base
的类型。
#include <concepts>
#include <vector>
using price_depth = int;
template<typename T>
concept LookupScheme = requires (const T& x, const std::vector<price_depth>& v) {
{x(v.begin(), v.end(), price_depth{})} -> std::same_as<decltype(v.begin())>;
};
namespace detail {
struct LowHigh {
template<typename ForwardIt, typename T>
decltype(auto) operator()(ForwardIt first, ForwardIt last, const T& value ) const {
return std::upper_bound(first, last, value);
}
};
struct HighLow {
template<typename ForwardIt, typename T>
decltype(auto) operator()(ForwardIt first, ForwardIt last, const T& value ) const {
return std::lower_bound(first, last, value);
}
};
template <LookupScheme Scheme>
struct ladder_base
{
using value_type = price_depth;
using ladder_type = std::vector< value_type >;
void insert(value_type v)
{
auto iter = Scheme::exec(std::begin(data_), std::end(data_), v);
data_.insert(iter, std::move(v));
}
protected:
std::vector< value_type > data_;
};
} // namespace detail
struct bid_ladder : detail::ladder_base< detail::LowHigh >
{
};
struct offer_ladder : detail::ladder_base< detail::HighLow >
{
};
您可以在标准库的排序容器中看到相同的方法,例如 std::map<>
的 Compare
参数。
template<class C>
auto lookup(C& c, value_type v) {
if constexpr(std::same_as<LookupScheme, LowHigh>)
return std::upper_bound(c.begin(), c.end(), v);
else if constexpr(std::same_as<LookupScheme, HighLow>)
return std::lower_bound(c.begin(), c.end(), v);
}
void insert(value_type v) {
auto iter = lookup(data_, v);
data_.insert(iter, std::move(v));
}
这些算法将比较对象作为它们的最后一个参数 - 因此您可以利用它来发挥自己的优势。
template < class Compare >
struct ladder_base
{
using value_type = price_depth;
using ladder_type = std::vector< value_type >;
void
insert(value_type v)
{
auto iter = std::upper_bound(data_.begin(), data_.end(), v, Compare{} );
data_.insert(iter, std::move(v));
}
protected:
std::vector< value_type > data_;
};
然后使用 ladder_base<std::less<>>
或 ladder_base<std::greater<>>
,具体取决于您想要的排序顺序。
请注意 std::lower_bound
和 std::upper_bound
不是反义词 ,因此您的原文并不完全正确。 lower_bound
为您提供第一个元素 >= x
,upper_bound
为您提供第一个元素 > x
。因此,从一个更改为另一个不会更改您的排序顺序(两者都需要递增顺序),只有比较对象会影响它。
例如:
std::vector<int> v = {1, 3, 5, 7};
auto i = std::lower_bound(v.begin(), v.end(), 3); // this is the 3
auto j = std::upper_bound(v.begin(), v.end(), 3); // this is the 5
请注意,向量是按递增顺序排序的,但两次调用都是完美的 well-formed。如果您想要反向排序,则必须将 std::greater{}
作为比较对象传入(如我所示)。
但无论哪种方式,您都希望使用 std::upper_bound
- 无论排序顺序如何。