Using C concepts, I'd like to be able to define concepts that force all the types in a parameter pack to either be a given type, or in a list of given types. The 4 concepts I've defined are:
type_is
: check if typeT
matches typeU
type_in
: check if typeT
is one of the types inU...
all_types_are
: check if all types in parameter packT...
match typeU
all_types_in
: check if all types in parameter packT...
are any of the types inU...
(can be different types inU...
)
The code, with a method for each concept to test agsinst:
#include <concepts>
#include <iostream>
#include <type_traits>
class A{public: int var = 100;};
class B{public: int var = 200;};
class C{public: int var = 300;};
template <typename TypeToCheck, typename TypeToCheckAgainst>
concept type_is = requires
{
requires std::same_as<std::remove_cvref_t<TypeToCheck>, TypeToCheckAgainst>;
};
template <typename TypeToCheck, typename ...TypesToCheckAgainst>
concept type_in = requires
{
requires (std::same_as<std::remove_cvref_t<TypeToCheck>, TypesToCheckAgainst> || ...);
};
template <typename ...TypesToCheck, typename TypeToCheckAgainst>
concept all_types_are = requires
{
requires (std::same_as<std::remove_cvref_t<TypeToCheckAgainst>, TypesToCheck> && ...);
};
template <typename ...TypesToCheck, typename ...TypesToCheckAgainst>
concept all_types_in = requires
{
requires (std::same_as<std::remove_cvref_t<TypesToCheck>, TypesToCheckAgainst> || ...);
};
auto method1(type_is<A> auto&& object)
{
std::cout << object.var;
std::cout << std::endl;
}
auto method2(type_in<A, B> auto&& object)
{
std::cout << object.var;
std::cout << std::endl;
}
auto method3(all_types_are<A> auto&&... objects)
{
(std::cout << ... << objects.var);
std::cout << std::endl;
}
auto method4(all_types_in<A, B> auto&&... objects)
{
(std::cout << ... << objects.var);
std::cout << std::endl;
}
int main()
{
A a;
B b;
C c;
method1(a);
method2(b);
method3(a, a, a);
method4(a, b, b);
return 0;
}
The first 2 concepts that check for a single type T
work fine, but for the last 2 concepts, if I call methods constrained with those concepts, with arguments that should conform to the concepts, they still fail with:
main.cpp(75): error C2672: 'method3': no matching overloaded function found
main.cpp(75): error C7602: 'method3': the associated constraints are not satisfied
main.cpp(53): note: see declaration of 'method3'
main.cpp(76): error C2672: 'method4': no matching overloaded function found
main.cpp(76): error C7602: 'method4': the associated constraints are not satisfied
main.cpp(60): note: see declaration of 'method4'
I'm not sure how to correct my last two concepts so that they behave as desired, as I don't understand how the constraints aren't satisfied. What do I need to change in order to make the concepts work?
CodePudding user response:
Your interpretation of the syntax in
auto method3(all_types_are<A> auto&&... objects)
is wrong. Using a type constraint in a template parameter pack doesn't result in the constraint being checked once for the whole pack expansion.
Instead the constraint will be checked for each element of the pack individually. So all you need is
auto method3(type_is<A> auto&&... objects)
to achieve what you intent all_types_are
to do.
It is not possible to use a type constraint to constraint multiple types at once. If you need this (which is not the case here) you will need to use a requires
clause instead, e.g.:
auto method4(auto&&... objects)
requires all_types_in<decltype(objects)..., A, B>
{ /*...*/ }
However, this will fail here as well, because there is no way to decide which of the template arguments in all_types_in<decltype(objects)..., A, B>
are supposed to be part of TypesToCheck
and which are supposed to be part of TypesToCheckAgainst
.
CodePudding user response:
First, there is no need to use nested requires
, just simple do
template <typename TypeToCheck, typename TypeToCheckAgainst>
concept type_is = std::same_as<
std::remove_cvref_t<TypeToCheck>, TypeToCheckAgainst>;
template <typename TypeToCheck, typename ...TypesToCheckAgainst>
concept type_in = (std::same_as<
std::remove_cvref_t<TypeToCheck>, TypesToCheckAgainst> || ...);
Second, you don't need to define all_types_are
additionally, just reuse type_is
/type_in
for the variadic template version
auto method3(type_is<A> auto&&... objects) {
(std::cout << ... << objects.var);
std::cout << std::endl;
}
auto method4(type_in<A, B> auto&&... objects) {
(std::cout << ... << objects.var);
std::cout << std::endl;
}