处理一般情况混合类型和非类型的可变参数模板

Handle variadic templates for general-case mixed types and non-types

本文关键字:类型 变参 参数 情况 混合 处理      更新时间:2023-10-16

所以我试图做一个类型特征,说明两个"外部"类类型是否相同。

即。std::vector<int>std::vector<double>一样,我不在乎我的类型特征的任何内部争论。

我在尝试为此制作泛型类型特征时遇到的一个问题是,我只知道如何将类型化的可变参数模板与非类型化的模板分开处理,这似乎阻止了我将其设为泛型。

是否可以处理类型化和非类型化模板参数的任何排列?

以下是我实现的内容(包括失败的示例(:

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

你能一般地做到这一点吗?不,还没有。但是有一些方法可以做到这一点(尽管它们不是最干净的(。

第一步是比较内部"值"。虽然结构模板参数不能完全按照你想要的方式执行,但函数模板参数可以(您可以为 typename/auto 创建"重载"(。

因此,有了这些知识,您可以执行以下操作:

enum version {
type, value
};
template <version...EE>
struct version_list {};
template <version...EE>
inline constexpr version_list<EE...> VL {};

template <template <class> class C, class T1>
constexpr auto decompose(C<T1>&)     { return VL<type>; }
template <template <auto> class C, auto V1>
constexpr auto decompose(C<V1>&)     { return VL<value>; }
template <template <class, class> class C, class T1, class T2>
constexpr auto decompose(C<T1, T2>&) { return VL<type, type>; }
template <template <class, auto> class C, class T1, auto V1>
constexpr auto decompose(C<T1, V1>&) { return VL<type, value>; }
/* continue repeating for different variations... */

这将允许您比较模板参数的数量/类型,如下所示:

template <typename T>
using decompose_t = decltype(decompose(std::declval<T&>()));
template <version...EE1, version...EE2>
constexpr bool compare_versions(
const version_list<EE1...>, 
const version_list<EE2...>) 
{
if constexpr(sizeof...(EE1) != sizeof...(EE2)) return false;
else return ((EE1 == EE2) && ...);
}
template <typename T, typename U>
inline constexpr bool compare_versions_v 
= compare_versions(decompose_t<T>{}, decompose_t<U>{});

最后,您可以像这样创建比较:

template <typename T, typename U>
constexpr bool is_outer_type_same() {
if(compare_versions_v<T, U>) {
/* Comparison logic */
}
else return false;
}
template <typename T, typename U>
inline constexpr bool is_outer_type_same_v = is_outer_type_same<T, U>();

如果外部类型没有相同的模板参数,您甚至不会尝试检查它们是否匹配。

我省略了实际的比较逻辑,因为它与您在第一次检查中所做的非常相似。您可能希望编写一些代码来生成所有这些函数并将它们写入文件,然后您可以将其包含/导入到实际公开的代码中。

但是,如果我们有typename auto:P,所有这些都可以避免