检查模板参数是否在同质参数包中出现两次以上
Check if a template parameter appears more than twice in a homogeneous parameter pack
我需要检查传递给函数的任何参数是否出现了两次以上。本质上,我想要以下行为
appearance<0,1,0,1>::value // should return true
appearance<2,0,1,2,2>::value // should return false
appearance<5,5,5>::value // should return false
我知道 上有一个问题,但它没有回答我的问题,因为该问题的解决方案 return 例如
no_unique<0,1,0,1,0,1>::value // returns 2
但是没有说参数是出现一次,两次还是三次。
我如何在 C++11
中实现这一点?
效率不高,但您可以使用以下方法:
#include <algorithm>
#include <type_traits>
template <size_t S, size_t... Sizes>
struct count;
template <size_t S>
struct count<S>: std::integral_constant<size_t, 0> {};
template <size_t S1, size_t... Sizes>
struct count<S1, S1, Sizes...>:
std::integral_constant<size_t, 1 + count<S1, Sizes...>{}> {};
template <size_t S1, size_t S2, size_t... Sizes>
struct count<S1, S2, Sizes...>:
count<S1, Sizes...> {};
template <size_t...>
struct max_count;
template <>
struct max_count<>: std::integral_constant<size_t, 0> { };
template <size_t S, size_t... Sizes>
struct max_count<S, Sizes...>:
std::integral_constant<size_t, std::max(1 + count<S, Sizes...>{},
max_count<Sizes...>::value)> { };
template <size_t... Sizes>
struct no_more_than_two: std::integral_constant<bool, max_count<Sizes...>{} <= 2> { };
static_assert(no_more_than_two<0,1,0,1>{}, "");
static_assert(!no_more_than_two<2,0,1,2,2>{}, "");
static_assert(!no_more_than_two<5,5,5>{}, "");
您首先检索任何值的最大出现次数,然后将其与 2 进行比较。
如果您只有 C++11(不是 C++14),请将 std::max
替换为自定义 ct_max
:
template <typename T>
constexpr const T& ct_max(T const& t1, T const& t2) {
return t1 < t2 ? t2 : t1;
}
我滥用多重继承的回答:
#include <utility>
#include <type_traits>
template <std::size_t I>
struct wrap {};
template <std::size_t ... Is>
struct derived : wrap<Is>...
{
};
template <>
struct derived<> {
};
template <typename, typename, std::size_t...>
struct appearance_impl;
template <std::size_t... I2, std::size_t... I1, std::size_t I, std::size_t...Is>
struct appearance_impl<derived<I2...>, derived<I1...>, I, Is...>
: std::conditional<
std::is_base_of<wrap<I>, derived<I2...>>::value, // If I in I2 => false
std::false_type,
typename std::conditional<
std::is_base_of<wrap<I>, derived<I1...>>::value, // else if I in I1, add I to I2
appearance_impl<derived<I, I2...>, derived<I1...>, Is...>,
appearance_impl<derived<I2...>, derived<I, I1...>, Is...>
>::type
>::type
{
};
template <typename D2, typename D1>
struct appearance_impl<D2, D1> : std::true_type {};
template <std::size_t ...Is>
struct appearance : appearance_impl<derived<>, derived<>, Is...>::type { };
int main() {
static_assert(appearance<0,1,0,1>::value, "");
static_assert(!appearance<2,0,1,2,2>::value, "");
static_assert(!appearance<5,5,5>::value, "");
}
这有点长,但应该很高效,避免了太多的聪明。
// Map integers to types.
template< int i >
using intc = std::integral_constant< int, i >;
// Increment a type-integer.
template< typename intc >
using inc_intc = std::integral_constant< int, intc::value + 1 >;
// Count the appearances of an integer in a sequence.
template< int ... >
struct appearances { // Base case of induction: return zero for every value.
template< int i >
static intc< 0 > count( intc< i > );
};
// Recursive case: Add one to the base class count.
template< int i, int ... rem >
struct appearances< i, rem ... >
: appearances< rem ... > {
typedef appearances< rem ... > base;
using base::count;
static inc_intc< decltype( base::count( intc< i >{} ) ) >
count( intc< i > );
};
// Find the sequences in question with early exit.
template< typename acc, typename seq, typename = void >
struct no_triple_occurrence_impl // Base case: empty sequence, no triples.
: std::true_type {};
// Early exit case: found two instances of the next i.
template< typename acc, int i, int ... rem >
struct no_triple_occurrence_impl<
acc,
std::integer_sequence< int, i, rem ... >,
std::enable_if_t< decltype(
acc::count( intc< i >{} )
){} >= 2 >
> : std::false_type {};
// Recursive case: add the next i to the counter.
template< int ... done, int i, int ... rem >
struct no_triple_occurrence_impl<
appearances< done ... >,
std::integer_sequence< int, i, rem ... >,
std::enable_if_t< decltype(
appearances< done ... >::count( intc< i >{} )
){} < 2 >
> : no_triple_occurrence_impl<
appearances< i, done ... >, // Keep done... at the end for memoization.
std::integer_sequence< int, rem ... >
>::type {};
template< int ... seq >
constexpr bool no_triple_occurrence
= no_triple_occurrence_impl<
appearances<>,
std::integer_sequence< int, seq ... >
>::value;
(on Coliru.)
我也可以玩吗?
以下是我的解决方案,主要基于部分模板专业化。
#include <iostream>
using IType = int; // or long? or unsigned? or size_t?
template <typename IT, IT ...>
struct intSeq
{ };
template <typename, typename, std::size_t>
struct no_more_than_val;
template <typename IT, IT I0, std::size_t M, IT Val, IT ... Is>
struct no_more_than_val<intSeq<IT, I0, Is...>, intSeq<IT, Val>, M>
{ static constexpr bool value
{ no_more_than_val<intSeq<IT, Is...>, intSeq<IT, Val>, M>::value }; };
template <typename IT, IT Val, std::size_t M, IT ... Is>
struct no_more_than_val<intSeq<IT, Val, Is...>, intSeq<IT, Val>, M>
{ static constexpr bool value
{ no_more_than_val<intSeq<IT, Is...>, intSeq<IT, Val>, M-1U>::value }; };
template <typename IT, IT I0, IT ... Is>
struct no_more_than_val<intSeq<IT, I0, Is...>, intSeq<IT, I0>, 0U>
{ static constexpr bool value { false }; };
template <typename IT, std::size_t M, IT Val>
struct no_more_than_val<intSeq<IT>, intSeq<IT, Val>, M>
{ static constexpr bool value { true }; };
template <typename, std::size_t>
struct no_more_than_list;
template <typename IT, std::size_t M>
struct no_more_than_list<intSeq<IT>, M>
{ static constexpr bool value { true }; };
template <typename IT, IT I0, IT ... Is>
struct no_more_than_list<intSeq<IT, I0, Is...>, 0U>
{ static constexpr bool value { false }; };
template <typename IT, std::size_t M, IT I0, IT ... Is>
struct no_more_than_list<intSeq<IT, I0, Is...>, M>
{
static constexpr bool value
{ no_more_than_val<intSeq<IT, Is...>, intSeq<IT, I0>, M-1U>::value
&& no_more_than_list<intSeq<IT, Is...>, M>::value };
};
template <IType ... Is>
struct appearance
: public no_more_than_list<intSeq<IType, Is...>, 2U>
{ };
int main()
{
std::cout << appearance<0,1,0,1>::value << std::endl; // print 1
std::cout << appearance<2,0,1,2,2>::value << std::endl; // print 0
std::cout << appearance<5,5,5>::value << std::endl; // print 0
}
如果你可以使用C++14,你可以使用std::integer_sequence
而不是intSeq
。无论如何,您可以使用 std::integral_constant<IT, Val>
而不是 intSeq<IT, Val>
。
使用 boost.hana:
的简短易读版本
#include <boost/hana/all_of.hpp>
#include <boost/hana/group.hpp>
#include <boost/hana/length.hpp>
#include <boost/hana/less.hpp>
#include <boost/hana/sort.hpp>
#include <boost/hana/tuple.hpp>
#include <boost/hana/type.hpp>
namespace hana = boost::hana;
template <int... ints>
struct appearance {
static constexpr bool value =
hana::all_of(
hana::transform(
hana::group(
hana::sort(
hana::make_tuple(hana::size_c<ints>...))),
hana::length),
hana::less.than(hana::size_c<3>));
};
我需要检查传递给函数的任何参数是否出现了两次以上。本质上,我想要以下行为
appearance<0,1,0,1>::value // should return true
appearance<2,0,1,2,2>::value // should return false
appearance<5,5,5>::value // should return false
我知道
no_unique<0,1,0,1,0,1>::value // returns 2
但是没有说参数是出现一次,两次还是三次。
我如何在 C++11
中实现这一点?
效率不高,但您可以使用以下方法:
#include <algorithm>
#include <type_traits>
template <size_t S, size_t... Sizes>
struct count;
template <size_t S>
struct count<S>: std::integral_constant<size_t, 0> {};
template <size_t S1, size_t... Sizes>
struct count<S1, S1, Sizes...>:
std::integral_constant<size_t, 1 + count<S1, Sizes...>{}> {};
template <size_t S1, size_t S2, size_t... Sizes>
struct count<S1, S2, Sizes...>:
count<S1, Sizes...> {};
template <size_t...>
struct max_count;
template <>
struct max_count<>: std::integral_constant<size_t, 0> { };
template <size_t S, size_t... Sizes>
struct max_count<S, Sizes...>:
std::integral_constant<size_t, std::max(1 + count<S, Sizes...>{},
max_count<Sizes...>::value)> { };
template <size_t... Sizes>
struct no_more_than_two: std::integral_constant<bool, max_count<Sizes...>{} <= 2> { };
static_assert(no_more_than_two<0,1,0,1>{}, "");
static_assert(!no_more_than_two<2,0,1,2,2>{}, "");
static_assert(!no_more_than_two<5,5,5>{}, "");
您首先检索任何值的最大出现次数,然后将其与 2 进行比较。
如果您只有 C++11(不是 C++14),请将 std::max
替换为自定义 ct_max
:
template <typename T>
constexpr const T& ct_max(T const& t1, T const& t2) {
return t1 < t2 ? t2 : t1;
}
我滥用多重继承的回答:
#include <utility>
#include <type_traits>
template <std::size_t I>
struct wrap {};
template <std::size_t ... Is>
struct derived : wrap<Is>...
{
};
template <>
struct derived<> {
};
template <typename, typename, std::size_t...>
struct appearance_impl;
template <std::size_t... I2, std::size_t... I1, std::size_t I, std::size_t...Is>
struct appearance_impl<derived<I2...>, derived<I1...>, I, Is...>
: std::conditional<
std::is_base_of<wrap<I>, derived<I2...>>::value, // If I in I2 => false
std::false_type,
typename std::conditional<
std::is_base_of<wrap<I>, derived<I1...>>::value, // else if I in I1, add I to I2
appearance_impl<derived<I, I2...>, derived<I1...>, Is...>,
appearance_impl<derived<I2...>, derived<I, I1...>, Is...>
>::type
>::type
{
};
template <typename D2, typename D1>
struct appearance_impl<D2, D1> : std::true_type {};
template <std::size_t ...Is>
struct appearance : appearance_impl<derived<>, derived<>, Is...>::type { };
int main() {
static_assert(appearance<0,1,0,1>::value, "");
static_assert(!appearance<2,0,1,2,2>::value, "");
static_assert(!appearance<5,5,5>::value, "");
}
这有点长,但应该很高效,避免了太多的聪明。
// Map integers to types.
template< int i >
using intc = std::integral_constant< int, i >;
// Increment a type-integer.
template< typename intc >
using inc_intc = std::integral_constant< int, intc::value + 1 >;
// Count the appearances of an integer in a sequence.
template< int ... >
struct appearances { // Base case of induction: return zero for every value.
template< int i >
static intc< 0 > count( intc< i > );
};
// Recursive case: Add one to the base class count.
template< int i, int ... rem >
struct appearances< i, rem ... >
: appearances< rem ... > {
typedef appearances< rem ... > base;
using base::count;
static inc_intc< decltype( base::count( intc< i >{} ) ) >
count( intc< i > );
};
// Find the sequences in question with early exit.
template< typename acc, typename seq, typename = void >
struct no_triple_occurrence_impl // Base case: empty sequence, no triples.
: std::true_type {};
// Early exit case: found two instances of the next i.
template< typename acc, int i, int ... rem >
struct no_triple_occurrence_impl<
acc,
std::integer_sequence< int, i, rem ... >,
std::enable_if_t< decltype(
acc::count( intc< i >{} )
){} >= 2 >
> : std::false_type {};
// Recursive case: add the next i to the counter.
template< int ... done, int i, int ... rem >
struct no_triple_occurrence_impl<
appearances< done ... >,
std::integer_sequence< int, i, rem ... >,
std::enable_if_t< decltype(
appearances< done ... >::count( intc< i >{} )
){} < 2 >
> : no_triple_occurrence_impl<
appearances< i, done ... >, // Keep done... at the end for memoization.
std::integer_sequence< int, rem ... >
>::type {};
template< int ... seq >
constexpr bool no_triple_occurrence
= no_triple_occurrence_impl<
appearances<>,
std::integer_sequence< int, seq ... >
>::value;
(on Coliru.)
我也可以玩吗?
以下是我的解决方案,主要基于部分模板专业化。
#include <iostream>
using IType = int; // or long? or unsigned? or size_t?
template <typename IT, IT ...>
struct intSeq
{ };
template <typename, typename, std::size_t>
struct no_more_than_val;
template <typename IT, IT I0, std::size_t M, IT Val, IT ... Is>
struct no_more_than_val<intSeq<IT, I0, Is...>, intSeq<IT, Val>, M>
{ static constexpr bool value
{ no_more_than_val<intSeq<IT, Is...>, intSeq<IT, Val>, M>::value }; };
template <typename IT, IT Val, std::size_t M, IT ... Is>
struct no_more_than_val<intSeq<IT, Val, Is...>, intSeq<IT, Val>, M>
{ static constexpr bool value
{ no_more_than_val<intSeq<IT, Is...>, intSeq<IT, Val>, M-1U>::value }; };
template <typename IT, IT I0, IT ... Is>
struct no_more_than_val<intSeq<IT, I0, Is...>, intSeq<IT, I0>, 0U>
{ static constexpr bool value { false }; };
template <typename IT, std::size_t M, IT Val>
struct no_more_than_val<intSeq<IT>, intSeq<IT, Val>, M>
{ static constexpr bool value { true }; };
template <typename, std::size_t>
struct no_more_than_list;
template <typename IT, std::size_t M>
struct no_more_than_list<intSeq<IT>, M>
{ static constexpr bool value { true }; };
template <typename IT, IT I0, IT ... Is>
struct no_more_than_list<intSeq<IT, I0, Is...>, 0U>
{ static constexpr bool value { false }; };
template <typename IT, std::size_t M, IT I0, IT ... Is>
struct no_more_than_list<intSeq<IT, I0, Is...>, M>
{
static constexpr bool value
{ no_more_than_val<intSeq<IT, Is...>, intSeq<IT, I0>, M-1U>::value
&& no_more_than_list<intSeq<IT, Is...>, M>::value };
};
template <IType ... Is>
struct appearance
: public no_more_than_list<intSeq<IType, Is...>, 2U>
{ };
int main()
{
std::cout << appearance<0,1,0,1>::value << std::endl; // print 1
std::cout << appearance<2,0,1,2,2>::value << std::endl; // print 0
std::cout << appearance<5,5,5>::value << std::endl; // print 0
}
如果你可以使用C++14,你可以使用std::integer_sequence
而不是intSeq
。无论如何,您可以使用 std::integral_constant<IT, Val>
而不是 intSeq<IT, Val>
。
使用 boost.hana:
的简短易读版本#include <boost/hana/all_of.hpp>
#include <boost/hana/group.hpp>
#include <boost/hana/length.hpp>
#include <boost/hana/less.hpp>
#include <boost/hana/sort.hpp>
#include <boost/hana/tuple.hpp>
#include <boost/hana/type.hpp>
namespace hana = boost::hana;
template <int... ints>
struct appearance {
static constexpr bool value =
hana::all_of(
hana::transform(
hana::group(
hana::sort(
hana::make_tuple(hana::size_c<ints>...))),
hana::length),
hana::less.than(hana::size_c<3>));
};