获取第一个模板参数的稳健方法
Robust method of getting the first template parameter
假设一个模板只有一个类型的模板参数列表,下面的类型特征可以提取第一个类型参数:
template<typename T>
struct front {};
template<template<typename...> typename C, typename FirstT, typename... Args>
struct front<C<FirstT, Args...>> {using type = FirstT;};
template<typename T>
using front_t = typename front<T>::type;
template<typename...> struct foo{};
using std::is_same_v;
static_assert(is_same_v<front_t<foo<int, double>>, int>); // Ok
static_assert(is_same_v<front_t<foo<int, double>>, double>); // Fail (as expected)
但是,这不适用于具有值参数的模板:
using std::array;
static_assert(is_same_v<front_t<array<int, 5>>, int>);
// error: no type named 'type' in 'struct front<std::array<int, 5> >'
好的,现在我也需要考虑任何值参数:
template<typename T>
struct front {};
// First parameter is a type, other parameters are types
template<template<typename...> typename C, typename FirstT, typename... Args>
struct front<C<FirstT, Args...>> {using type = FirstT;};
// First parameter is a type, other parameters are values
template<template<typename, auto...> typename C, typename FirstT, auto... Args>
struct front<C<FirstT, Args...>> {using type = FirstT;};
// First parameter is a value, other parameters are types
template<template<auto, typename...> typename C, auto FirstA, typename... Args>
struct front<C<FirstA, Args...>> {constexpr static const auto value = FirstA;};
// First parameter is a value, other parameters are values
template<template<auto...> typename C, auto FirstA, auto... Args>
struct front<C<FirstA, Args...>> {constexpr static const auto value = FirstA;};
// Avoid ambiguity if there's only a single type parameter
template<template<typename...> typename C, typename FirstT>
struct front<C<FirstT>> {using type = FirstT;};
// Avoid ambiguity if there's only a single value parameter
template<template<auto...> typename C, auto FirstA>
struct front<C<FirstA>> {constexpr static const auto value = FirstA;};
template<typename T>
using front_t = typename front<T>::type;
template<typename T>
const auto front_v = front<T>::value;
template<typename...> struct foo{};
template<auto...> struct bar{};
static_assert(std::is_same_v<front_t<foo<int>>, int>); // Ok
static_assert(std::is_same_v<front_t<foo<int, double>>, double>); // Fail (as expected)
static_assert(std::is_same_v<front_t<std::array<int, 5>>, int>); // Ok
static_assert(front_v<bar<5, 4>> == 5); // Ok
static_assert(front_v<bar<5, 4>> == 4); // Fail (as expected)
但后来我又尝试了一些混音':
template<typename, typename, auto...> struct baz{};
static_assert(std::is_same_v<front_t<baz<int, int>>, int>);
// error: no type named 'type' in 'struct front<baz<int, int> >'
这显然已经失控了。现在,我不仅要担心混合类型和值参数,我还要担心这些参数的顺序,并为它们的每个组合编写一个专门化。但我想要的只是这些参数中的第一个!其他的根本不重要。
最后,问题是:我可以一般地 "ignore" 任何我不需要的模板参数吗? "generically" 我的意思是忽略值和类型。
没有
作为一般规则,使用任意模板使其参数有意义的方式不是一个好主意。这只是您将 运行 遇到的众多问题之一。
模板参数是位置性的,但对于每个位置并没有普遍认同的含义。容器的第一个参数往往是值类型,但即使是关联容器也不是这样(前两个参数被合成为值类型)。
template<class T>
using value_type = typename T::value_type;
捕获了一堆案例。如果我们想获取关联容器的键类型,我们可以这样做:
template<class T>
using key_type = typename T::key_type;
namespace details {
template<class...>using void_t=void;
template<template<class...>class Z, class, class...Ts>
struct can_apply:std::false_type {};
template<template<class...>class Z, class...Ts>
struct can_apply<Z,void_t<Z<Ts...>>, Ts...>:std::true_type {};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z,void,Ts...>;
template<template<class>class Z>
struct ztemplate1 {
template<class T>
using result = Z<T>;
};
template<bool b, template<class>class True, template<class> class False, class T>
using conditional_apply1 = typename std::conditional_t<
b,
ztemplate1<True>,
ztemplate1<False>
>::template result<T>;
template<class X>
using container_first_type = conditional_apply1<
can_apply<key_type, X>{},
key_type,
value_type,
X
>;
现在 container_first_type<std::map<std::string, int>>
是 std::string
,而 container_first_type<std::vector<int>>
是 int
,container_first_type<std::array<double, 7>>
是 double
。
无法处理每个案例。
您可以采取的解决方法:
在 class
中提供 typedef
template <typename T /*, */> class Foo {
using first_type = T;
}
仅使用类型,可能 std::integral_constant
Bar<int, std::true_type, std::integral_constant<int, 42>> bar;
只处理常见情况如array
.
假设一个模板只有一个类型的模板参数列表,下面的类型特征可以提取第一个类型参数:
template<typename T>
struct front {};
template<template<typename...> typename C, typename FirstT, typename... Args>
struct front<C<FirstT, Args...>> {using type = FirstT;};
template<typename T>
using front_t = typename front<T>::type;
template<typename...> struct foo{};
using std::is_same_v;
static_assert(is_same_v<front_t<foo<int, double>>, int>); // Ok
static_assert(is_same_v<front_t<foo<int, double>>, double>); // Fail (as expected)
但是,这不适用于具有值参数的模板:
using std::array;
static_assert(is_same_v<front_t<array<int, 5>>, int>);
// error: no type named 'type' in 'struct front<std::array<int, 5> >'
好的,现在我也需要考虑任何值参数:
template<typename T>
struct front {};
// First parameter is a type, other parameters are types
template<template<typename...> typename C, typename FirstT, typename... Args>
struct front<C<FirstT, Args...>> {using type = FirstT;};
// First parameter is a type, other parameters are values
template<template<typename, auto...> typename C, typename FirstT, auto... Args>
struct front<C<FirstT, Args...>> {using type = FirstT;};
// First parameter is a value, other parameters are types
template<template<auto, typename...> typename C, auto FirstA, typename... Args>
struct front<C<FirstA, Args...>> {constexpr static const auto value = FirstA;};
// First parameter is a value, other parameters are values
template<template<auto...> typename C, auto FirstA, auto... Args>
struct front<C<FirstA, Args...>> {constexpr static const auto value = FirstA;};
// Avoid ambiguity if there's only a single type parameter
template<template<typename...> typename C, typename FirstT>
struct front<C<FirstT>> {using type = FirstT;};
// Avoid ambiguity if there's only a single value parameter
template<template<auto...> typename C, auto FirstA>
struct front<C<FirstA>> {constexpr static const auto value = FirstA;};
template<typename T>
using front_t = typename front<T>::type;
template<typename T>
const auto front_v = front<T>::value;
template<typename...> struct foo{};
template<auto...> struct bar{};
static_assert(std::is_same_v<front_t<foo<int>>, int>); // Ok
static_assert(std::is_same_v<front_t<foo<int, double>>, double>); // Fail (as expected)
static_assert(std::is_same_v<front_t<std::array<int, 5>>, int>); // Ok
static_assert(front_v<bar<5, 4>> == 5); // Ok
static_assert(front_v<bar<5, 4>> == 4); // Fail (as expected)
但后来我又尝试了一些混音':
template<typename, typename, auto...> struct baz{};
static_assert(std::is_same_v<front_t<baz<int, int>>, int>);
// error: no type named 'type' in 'struct front<baz<int, int> >'
这显然已经失控了。现在,我不仅要担心混合类型和值参数,我还要担心这些参数的顺序,并为它们的每个组合编写一个专门化。但我想要的只是这些参数中的第一个!其他的根本不重要。
最后,问题是:我可以一般地 "ignore" 任何我不需要的模板参数吗? "generically" 我的意思是忽略值和类型。
没有
作为一般规则,使用任意模板使其参数有意义的方式不是一个好主意。这只是您将 运行 遇到的众多问题之一。
模板参数是位置性的,但对于每个位置并没有普遍认同的含义。容器的第一个参数往往是值类型,但即使是关联容器也不是这样(前两个参数被合成为值类型)。
template<class T>
using value_type = typename T::value_type;
捕获了一堆案例。如果我们想获取关联容器的键类型,我们可以这样做:
template<class T>
using key_type = typename T::key_type;
namespace details {
template<class...>using void_t=void;
template<template<class...>class Z, class, class...Ts>
struct can_apply:std::false_type {};
template<template<class...>class Z, class...Ts>
struct can_apply<Z,void_t<Z<Ts...>>, Ts...>:std::true_type {};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z,void,Ts...>;
template<template<class>class Z>
struct ztemplate1 {
template<class T>
using result = Z<T>;
};
template<bool b, template<class>class True, template<class> class False, class T>
using conditional_apply1 = typename std::conditional_t<
b,
ztemplate1<True>,
ztemplate1<False>
>::template result<T>;
template<class X>
using container_first_type = conditional_apply1<
can_apply<key_type, X>{},
key_type,
value_type,
X
>;
现在 container_first_type<std::map<std::string, int>>
是 std::string
,而 container_first_type<std::vector<int>>
是 int
,container_first_type<std::array<double, 7>>
是 double
。
无法处理每个案例。
您可以采取的解决方法:
在 class
中提供 typedeftemplate <typename T /*, */> class Foo { using first_type = T; }
仅使用类型,可能
std::integral_constant
Bar<int, std::true_type, std::integral_constant<int, 42>> bar;
只处理常见情况如
array
.