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;
}