In C you can make concepts that check for specific type equality:
template<typename T> concept Int = std::is_same_v<T, int>;
template<typename T> concept String = std::is_same_v<T, std::string>;
Is it possible to make a concept that checks for type equality of any type, so I could make templates looking something like this:
template<ValidType<int>... Ints> void passInts(Ints... ints) {}
template<ValidType<std::string>... Strings> void passStrings(Strings... strings) {}
That way I would only need to write a single concept checking for type equality. I know I could use conjunction
for this but I think concepts are much cleaner.
CodePudding user response:
I mean, you could write a concept to do that. Or you could just use std::same_as
:
template<std::same_as<int>... Ints> void passInts(Ints... ints)
template<std::same_as<std::string>... Strings> void passStrings(Strings... strings)
When a concept is used against a template parameter pack, it is applied individually to each of the types in the pack.
Of course, this may not quite do what you need. The reason being that same_as
is very serious about its name. If you want to invoke perfect forwarding through forwarding references, template argument deduction can start deducing the types as references to T
. Which re not the "same_as
" T
.
So if you want to allow for perfect forwarding, you need a different concept: similar_to
. Which sadly is not in the standard library, despite how simple and useful it is:
template<typename T1, typename T2>
concept similar_to = std::same_as<std::remove_cvref_t<T1>, T2>;
Note that this assumes that T2
has no reference or cv qualifiers. You can make a version that removes cvref from both.
Now, you can do this:
template<similar_to<int> ...Ints> void passInts(Ints && ...ints)
template<similar_to<std::string> ...Strings> void passStrings(Strings && ...strings)
And now you get perfect forwarding back.
CodePudding user response:
Not sure I understand exactly what you want but you can add the type you want to check for as second template parameter:
template<typename T, typename U> concept ValidType = std::is_same_v<T, U>;
Then your two examples will work in that all template arguments will be restricted to int
or std::string
respectively.
In fact that is exactly what the std::same_as
concept from the standard library already does. So just use that instead of ValidType
to get additional benefits of constraint subsumption lacking in the simple variant above.