I wrote type checking constexpr function.
It the type is type1
or type2
then returns true, otherwise returns false.
Here is the code. It works as I expected.
#include <type_traits>
struct type1{};
struct type2{};
struct type3{};
template <typename T>
constexpr bool is_type1or2() {
return std::is_same_v<T, type1> || std::is_same_v<T, type2>;
}
static_assert(is_type1or2<type1>());
static_assert(is_type1or2<type2>());
static_assert(!is_type1or2<type3>());
int main(){}
https://godbolt.org/z/dncKo1Pbb
Now, type1
is changed to template that has non-type parameter.
How to do the same type checking?
#include <type_traits>
template <std::size_t N>
struct type1{};
struct type2{};
struct type3{};
template <typename T>
constexpr bool is_type1or2() {
return std::is_same_v<T, type2>;
}
template <typename T, std::size_t N>
constexpr bool is_type1or2() {
return std::is_same_v<T, type1<N>>;
}
// I want to write as follows but I couldn't find a way, so far.
// static_assert(is_type1or2<type1<42>>());
// so I pass explicit second template argument 42.
static_assert(is_type1or2<type1<42>, 42>());
static_assert(is_type1or2<type2>());
static_assert(!is_type1or2<type3>());
int main(){}
https://godbolt.org/z/G1o5447z8
I tried but I can't eliminate the second template argument. It avoids generic code.
Is there any good way to check the type is type1<anyN>
or type2
?
In my actual case, I have 20 of non template types like type2
and 20 of template types like type1
. And half of them need to match. I want to avoid code repeatation as long as I can.
Clarify requirement
For template type type1<N>
, N
is not important. Both template is type1
is important. So the result of is_type1or2<type1<10>>()
and is_type1or2<type1<20>>()
are always same. I don't need to define individual template argument specialization based matching.
CodePudding user response:
You can make your own type trait:
template <typename>
struct is_type1 : public std::false_type {};
template <std::size_t N>
struct is_type1<type1<N>> : public std::true_type {};
This still requires the usage to instantiate an actual type:
static_assert(is_type1or2<type1<42>>());
You can also do it this way:
template <template<std::size_t> class>
struct is_type1 : public std::false_type {};
template <>
struct is_type1<type1> : public std::true_type {};
But then is_type1
can only be instantiated with templates that have a std::size_t
parameter. So you can no longer do is_type1<T>::value || std::is_same_v<T, type2>
because is_type1
only accepts templates while std::is_same_v
only accepts types, and T
cannot be both. It can work if type2
is also defined like type1
, that is:
template <std::size_t N>
struct type1{};
template <std::size_t N>
struct type2{};
template <template<std::size_t> class>
struct is_type1or2_t : public std::false_type {};
template <>
struct is_type1or2_t<type1> : public std::true_type {};
template <>
struct is_type1or2_t<type2> : public std::true_type {};
template <template<std::size_t> class T>
constexpr bool is_type1or2() {
return is_type1or2_t<T>::value;
}
But you still can't use is_type1or2
with type3
, even in a static_assert
. So you also need to add an overload for simple types:
template <typename>
constexpr bool is_type1or2() {
return false;
}
If you also need an instantiation of type1
or type2
to match, you can add yet another type trait:
template <typename>
struct is_type1or2_instantiation_t : public std::false_type {};
template <std::size_t N>
struct is_type1or2_instantiation_t<type1<N>> : public std::true_type {};
template <std::size_t N>
struct is_type1or2_instantiation_t<type2<N>> : public std::true_type {};
// [...]
template <typename T>
constexpr bool is_type1or2() {
return is_type1or2_instantiation_t<T>::value;
}
CodePudding user response:
This can be solved easily if you change the syntax of the static_assert
to accept the type as a function argument, as this will allow function template argument deduction (see Takatoshi Kondo's answer).
However, this can also be solved by writing a template that checks whether a type is an instantiation of a template:
template<template <std::size_t> typename, typename>
struct is_instance_of : std::false_type {};
template<template <std::size_t> typename T, std::size_t N>
struct is_instance_of<T, T<N>> : std::true_type {};
Now is_instance_of
can be used in the function (without argument deduction) as:
template <typename T>
constexpr bool is_type1or2() {
return
is_instance_of<type1, T>::value // repeat for other templates that take a size_t parameter
or std::is_same_v<T, type2>; // repeat for non-template types
}
If you have other template types that you want to allow (i.e. templates that take parameters other than a size_t
), you can edit is_instance_of
as needed.
Here's a demo.
CodePudding user response:
After some of try and error, I finally found the solution.
In my actual case, I have 20 of non template types like type2 and 20 of template types like type1. And half of them need to match. I want to avoid code repeatation as long as I can.
In order to demonstrate my situation above, I added type4
non-type template similar to type1
. And type1
, type2
and type4
need to match.
#include <type_traits>
template <std::size_t N>
struct type1{};
struct type2{};
struct type3{};
template <std::size_t N> // type4 added
struct type4{};
// overload for non-templates
template <typename T>
constexpr bool is_type1or2or4(T) { // or 4 added
return std::is_same_v<T, type2>;
}
// overload for templates
template <template <std::size_t> typename T, std::size_t N>
constexpr bool is_type1or2or4(T<N>) { // or 4 added
return
std::is_same_v<T<N>, type1<N>> ||
std::is_same_v<T<N>, type4<N>>;
}
static_assert(is_type1or2or4(type1<42>()));
static_assert(is_type1or2or4(type2()));
static_assert(!is_type1or2or4(type3()));
static_assert(is_type1or2or4(type4<12>()));
int main(){}
https://godbolt.org/z/17GbGK738
I use template argument deduction. Then I can extract N and pass it as template argument. There are two overload funtion templates. One of for non-template types, the other is template types. It works fine.