Was wondering what syntax would be the most effective/convenient for concepts
on parameters pack
, including values template-parameters
when types (ou values' types) depend on each other?
For instance, with the following function :
equal_v<values...>
is true when all values are equal
template <auto... values>
constexpr static auto equal_v = []() consteval
{
static_assert(sizeof...(values) > 0, "equal_v : no arguments");
constexpr auto first_value = std::get<0>(std::tuple{values...});
static_assert(
(std::equality_comparable_with<decltype(values), decltype(first_value)> && ...),
"equal_v : cannot compare values");
return ((values == first_value) && ...);
}
();
I would have :
template <typename ... Ts>
concept are_equality_comparable = requires(Ts ... values)
{
{
std::conditional_t<(std::equality_comparable_with<decltype(std::get<0>(std::tuple{values...})), decltype(values)> && ...), std::true_type, std::false_type>{}
} -> std::same_as<std::true_type>;
};
template <auto ... values>
requires(are_equality_comparable<decltype(values)...>)
constexpr static auto equal_v = []() consteval {
static_assert(sizeof...(values) > 0, "equal_v : no arguments");
constexpr auto first_value = std::get<0>(std::tuple{values...});
return ((values == first_value) && ...);
}();
Which is kinda ugly, using std::conditional<..., std::true_type, std::false_type>
as std::same_as<..., std::true_type>
arguments.
So I ends up with the following syntax, which I think is the most elegant :
template <auto first_value, auto ... values>
requires (std::equality_comparable_with<decltype(first_value), decltype(values)> && ...)
constexpr static auto equal_v = []() consteval {
return ((values == first_value) && ...);
}();
or, with not mandatory first parameter :
template <auto ... values>
requires (std::equality_comparable_with<decltype(std::get<0>(std::tuple{values...})), decltype(values)> && ...)
constexpr static auto equal_v = []() consteval {
return ((values == std::get<0>(std::tuple{values...})) && ...);
}();
However, as all values depends on the first one,
I did not figure out a way using such hypothetic syntax yet :
template <auto first_value, std::equality_comparable_with<decltype(first_value)> ... values>
constexpr static auto equal_v = []() consteval {
return ((values == first_value) && ...);
}();
So, here are some points :
requires
clause?
e.g template <auto ... values>
instead of template <auto first_value, auto ... values>
[EDIT]
As pinpointed by artyer in the comments, concept auto value
is a valid syntax.
Also, this does not work with MSVC 19.28 as it results in :
error C7601: the associated constraints are not satisfied
nor GCC 10.2 :
error: placeholder constraints not satisfied
But works fine with Clang 11.0.1.
template <auto first_value, std::equality_comparable_with<decltype(first_value)> auto ... values> constexpr static auto equal_v = []() consteval {
return ((values == first_value) && ...);
}();
[EDIT_2]
In order to make equal_v<>
true :
template <auto first_value = int{}, std::equality_comparable_with<decltype(first_value)> auto ... values>
constexpr static auto equal_v = []() consteval {
return ((values == first_value) && ...);
}();
Well, as conclusion, here are answers to the two questions :
How to apply constraints on template-value-parameters ?
As pin-pointed by @Artyer and @Davis Herring, concept auto value
is a legal syntax defined in p1141r2.
NB : It's not currently part of cppreference documentation.
How to apply constraints on parameters-packs, when types depend on each-others ?
Using constrained-auto (concept (auto) arg
syntax) :
Defining the first one with a default value (to preserve parameterless resolutions),
and applying the contraints on a parameter-pack that contain parameters which remain.
template <auto first_value = int{},
std::equality_comparable_with<decltype(first_value)> auto ... values>
constexpr static auto equal_v = []() consteval {
return ((values == first_value) && ...);
}();
Using requires
-clause syntax :
template <auto ... values>
requires (std::equality_comparable_with<
decltype(std::get<0>(std::tuple{values...})),
decltype(values)
> && ...)
constexpr static auto equal_v = []() consteval {
return ((values == std::get<0>(std::tuple{values...})) && ...);
}();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With