Jul 22, 2021 at 8:44am UTC
Both have problems with an even number of types. Consider this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
template <class Opt>
constexpr bool has() {
return tuple_helper::tuple_contains_type<Opt, constraints_type>::value;
}
struct Xx{};
template <class O1 = Xx>
constexpr bool has_every() {
return has<O1>(); // std::is_same<O1, Xx>::value
}
template <class O1, class O2, class ... Opts>
constexpr bool has_every() {
return has<O1>() && has<O2>() && has_every<Opts...>();
}
int main()
{
struct X1{};
struct X2{};
struct X3{};
struct X4{};
has_every<X1, X2, X3, X4>();
return 0;
}
Or since 17:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
template <class Opt>
constexpr bool has() {
return tuple_helper::tuple_contains_type<Opt, constraints_type>::value;
}
template <class O1, class ... Opts>
constexpr bool has_every() {
if constexpr (sizeof ...(Opts) > 0)
return has<O1>() && has_every<Opts...>();
else
return has<O1>();
}
int main()
{
struct X1{};
struct X2{};
struct X3{};
struct X4{};
has_every<X1, X2, X3, X4>();
return 0;
}
Last edited on Jul 22, 2021 at 8:57am UTC
Jul 22, 2021 at 11:56am UTC
Your solutions @coder777 also have problems with even number of types? Or are these your proposals for correct implementation?
Seems to me your C++17 solution is correct! not sure about the other one...
Last edited on Jul 22, 2021 at 11:57am UTC
Jul 22, 2021 at 1:17pm UTC
As a matter of fact your 17 solution IS correct; not the first code..
Jul 22, 2021 at 5:07pm UTC
C++17
1 2
template <typename ... Elts>
constexpr bool has_every() const noexcept { return (has<Elts>() && ...); }
An implementation (as namespace scope functions) might look somewhat like this:
1 2 3 4 5 6 7 8 9 10
template <typename ...> struct tuple_contains;
// could also inherit `std::conjunction</*...*/>`
template <typename ... Elts, typename T>
struct tuple_contains<std::tuple<Elts...>, T>
: std::bool_constant<(std::is_same_v<T, Elts> || ...)> {};
template <typename Tuple, typename T>
bool constexpr tuple_contains_v = tuple_contains<Tuple, T>();
template <typename Tuple, typename ... Ts>
bool constexpr tuple_contains_all_v = (tuple_contains_v<Tuple, Ts> && ...);
Last edited on Jul 22, 2021 at 5:10pm UTC
Jul 23, 2021 at 3:13pm UTC
It's a pack expansion called a
fold expression . Specifically a unary right fold.
In general the compiler expands
(has<Elts>() && ...) into
(has<Elts1>() && (has<Elts2>() && (... && (has<EltsN-1>() && has<EltsN>()))))
For example
- if
Elts is the parameter pack containing
A, B, C, D , then
(has<Elts>() && ...) expands into
(has<A>() && (has<B>() && (has<C>() && has<D>())))
- If Elts is the parameter pack containing just one parameter
A , then
(has<Elts>() && ...) expands into
(has<A>())
- If Elts is the empty parameter pack
(has<Elts>() && ...) expands into
true , a special case.
https://en.cppreference.com/w/cpp/language/fold
https://www.foonathan.net/2020/05/fold-tricks/
Last edited on Jul 23, 2021 at 3:15pm UTC
Jul 26, 2021 at 5:43am UTC
Okay, thank you for the explanation.