So I'm trying to make a type trait that says whether two "outer" class types are the same.
ie. std::vector<int>
is the same as std::vector<double>
, I don't care about any inner arguments for my type trait.
A problem that I had with trying to make a generic type trait for this is that I only know how to handle the typed variadic templates separately to the non-typed ones, which appears to stop me from making it generic.
Is it possible to handle any permutation of typed and non-typed template arguments?
Here's what I've implemented (including an example of where it fails):
// g++ -std=c++17
#include <iostream>
#include <vector>
#include <array>
#include <type_traits>
// If the outer types don't match
template <typename, typename>
struct is_outer_type_same : std::false_type {};
// if the arguments of the compared Type contains only types
// ie. std::vector<int,std::allocator<int>>
// (the inner arguments are also types)
template <template<typename...> typename OuterType,
typename... InnerTypes1,
typename... InnerTypes2
>
struct is_outer_type_same < OuterType<InnerTypes1...>,
OuterType<InnerTypes2...>
>
: std::true_type {};
// if the arguments of the compared Type contains only non-types
// eg. SomeClassName<4,5,2>
template <template<auto...> typename OuterType,
auto... InnerVariables1,
auto... InnerVariables2
>
struct is_outer_type_same < OuterType<InnerVariables1...>,
OuterType<InnerVariables2...>
>
: std::true_type {};
// if the arguments of the compared Type contains a single
// typed-argument followed by some non-typed arguments
// ie. std::array<int, 4>
template <template<typename, auto...> typename OuterType,
typename InnerType1, auto... InnerVariables1,
typename InnerType2, auto... InnerVariables2
>
struct is_outer_type_same < OuterType<InnerType1, InnerVariables1...>,
OuterType<InnerType2, InnerVariables2...>
>
: std::true_type {};
// For any outer types whose arguments have the pattern:
// single variable argument followed by a sequence of typed arguments:
template <template<auto, typename...> typename OuterType,
auto InnerVariable1, typename... InnerTypes1,
auto InnerVariable2, typename... InnerTypes2
>
struct is_outer_type_same < OuterType<InnerVariable1, InnerTypes1...>,
OuterType<InnerVariable2, InnerTypes2...>
>
: std::true_type {};
// This is to make it neater to evaluate:
template <typename S, typename T>
inline constexpr bool is_outer_type_same_v
= is_outer_type_same<S,T>::value;
// Example type that fails to be handled
// correctly by the above struct templates:
template <typename A, typename B, int C>
struct ExampleType
{
A data1;
B data2;
const int data3 = C;
};
int main ()
{
// Examples to show where it fails:
std::cout << "Fails to find match for ExampleType: "
<< is_outer_type_same_v<ExampleType<double,int,2>,
ExampleType<double,int,2>>
<< std::endl << std::endl
// Examples to show where it works:
<< "Finds correctly: " << std::endl
<< std::endl << "Matches for std::vector: "
<< is_outer_type_same_v<std::vector<int>,
std::vector<double>>
<< is_outer_type_same_v<std::vector<std::vector<int>>,
std::vector<double>>
<< is_outer_type_same_v<std::vector<std::array<int,3>>,
std::vector<double>>
<< std::endl << "Mismatches for std::vector: "
<< is_outer_type_same_v<int,
std::vector<int>>
<< is_outer_type_same_v<std::array<int, 3>,
std::vector<int>>
<< is_outer_type_same_v<std::array<std::vector<int>, 3>,
std::vector<int>>
<< std::endl << "Matches for std::array: "
<< is_outer_type_same_v<std::array<int, 3>,
std::array<double, 7>>
<< is_outer_type_same_v<std::array<std::vector<int>, 7>,
std::array<double, 2>>
<< is_outer_type_same_v<std::array<std::array<int,3>, 8>,
std::array<std::vector<double>, 5>>
<< std::endl << "Mismatches for std::array: "
<< is_outer_type_same_v<int,
std::array<int,2>>
<< is_outer_type_same_v<std::vector<int>,
std::array<int,8>>
<< is_outer_type_same_v<std::vector<std::array<int,3>>,
std::array<int,2>>
<< std::endl;
return 0;
}
auto...
will work in most cases. – Geometrizestd::array
is a major use case and there are plenty of classes out there like it for matrices and things of the like. – Incompletestd::array
styled things will work with the double-pack solution given here. It's not rare to mix, it rare to mix in the form ofnon-type, type, non-type
, where astypes.., non-types...
is fairly common. – Geometrizetypes..., non-types...
? When I try to do that I always get errors. It’s not as generic as original question, but an answer to it would make my solution much better than what I have now... – Teniafugetype, non-types...
,type, type, non-types...
ect. Not very clean code, but once you stuff it away in your header you hpefully don't have to look at it very often. :-) – Geometrizenon-type
can be a pack, but the leading types would need one specialization each.type, non-types...
andtype, type, non-type...
would be 2 specialization. If you add 10 you should have the behaviour you want with some arguably ugly code. – Geometrizeauto
template parameters. – Cratchtype
. I'm not interested n the firsttype
/non-type
of the list of args, instead i want to ignore the args entirely - I dont care about them in this case. – Teniafuge