匹配的变异非类型模板
Matching variadic non-type templates
假设我有两个结构, Foo
和 Bar
:
template<int...>
struct Foo{};
template<unsigned long...>
struct Bar{};
我想创建一个类型特征(称为match_class
(,如果我通过两种Foo<...>
类型或两种Bar<...>
类型,则返回true,但是如果我尝试混合它们,则为错误:
int main()
{
using f1 = Foo<1, 2, 3>;
using f2 = Foo<1>;
using b1 = Bar<1, 2, 3>;
using b2 = Bar<1>;
static_assert(match_class<f1, f2>::value, "Fail");
static_assert(match_class<b1, b2>::value, "Fail");
static_assert(!match_class<f1, b1>::value, "Fail");
}
对于C 1z(Clang 5.0.0和GCC 8.0.0(,这足以执行此操作(演示(:
template<class A, class B>
struct match_class : std::false_type{};
template<class T, template<T...> class S, T... U, T... V>
struct match_class<S<U...>, S<V...>> : std::true_type{};
但是在C 14中,我会收到以下错误(相同的编译器*演示(:
error: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used [-Wunusable-partial-specialization]
struct match_class<S<U...>, S<V...>> : std::true_type{};
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
note: non-deducible template parameter 'T'
template<class T, template<T...> class S, T... U, T... V>
问题:C 14中的解决方法是什么?
理想情况下,用于测试类型特征的语法应保持不变。
次要问题: C 的行为14正确吗?(或者我看到的C 17未指定的行为?(
*请注意,MSVC 19.00.23506具有相同类型的故障演示
在C 14中,您无法推论T
在:
template<class T, template<T...> class S, T... U, T... V>
struct match_class<S<U...>, S<V...>> : std::true_type{};
但是在C 17中,您可以。您看到的行为是正确的。
在C 14中,由于您无法推断T
,因此您需要一种明确提供它的方法。因此,您可能需要类模板自己来指示其非类型模板参数类型:
template <int...> struct Foo { using type = int; };
template <unsigned long...> struct Bar { using type = unsigned long; };
或为此具有外部特征。然后明确写出所有内容 - 如果具有相同的非类型模板参数和,则两个类模板匹配,然后也具有相同的类模板:
template <class... Ts> struct make_void { using type = void; };
template <class... Ts> using void_t = typename make_void<Ts...>::type;
template <class T1, class T2, class A, class B>
struct match_class_impl : std::false_type { };
template <class T, template <T...> class S, T... U, T... V>
struct match_class_impl<T, T, S<U...>, S<V...>> : std::true_type{};
template <class A, class B, class=void>
struct match_class : std::false_type { };
template <class A, class B>
struct match_class<A, B, void_t<typename A::type, typename B::type>>
: match_class_impl<typename A::type, typename B::type, A, B>
{ };
这是增加对template auto
支持的结果。在C 14中,[temp.deduct.type]包含:
不能从非类型模板argument的类型中推导一个模板类型参数。 [示例:
template<class T, T i> void f(double a[10][i]); int v[10][20]; f(v); // error: argument for template-parameter T cannot be deduced
- end示例]
但是在C 17中,它现在读取:
当与相关类型声明的非类型模板参数
P
相对应的参数的值是从表达式推导的,而P
类型中的模板参数是从值类型中推导的。 [示例:template<long n> struct A { }; template<typename T> struct C; template<typename T, T n> struct C<A<n>> { using Q = T; }; using R = long; using R = C<A<2>>::Q; // OK; T was deduced to long from the // template argument value in the type A<2>
- 结束示例] 类型
T[N]
中的N
的类型为std::size_t
。 [示例:template<typename T> struct S; template<typename T, T n> struct S<int[n]> { using Q = T; }; using V = decltype(sizeof 0); using V = S<int[42]>::Q; // OK; T was deduced to std::size_t from the type int[42]
- 结束示例]
问题:C 14中的解决方法是什么?
C 14中可能的解决方法基于特征。
作为一个最小的,有效的例子(甚至可能很愚蠢,但它有助于获得这个想法(:
#include <type_traits>
#include <utility>
template<int...>
struct Foo{};
template<unsigned long...>
struct Bar{};
template<typename>
struct traits;
template<int... V>
struct traits<Foo<V...>> { using type = Foo<0>; };
template<unsigned long... V>
struct traits<Bar<V...>> { using type = Bar<0>; };
template<typename T, typename U>
constexpr bool match = std::is_same<typename traits<T>::type, typename traits<U>::type>::value;
int main() {
using f1 = Foo<1, 2, 3>;
using f2 = Foo<1>;
using b1 = Bar<1, 2, 3>;
using b2 = Bar<1>;
static_assert(match<f1, f2>, "Fail");
static_assert(match<b1, b2>, "Fail");
static_assert(!match<f1, b1>, "Fail");
}
作为旁注,在C 17中,您可以按照以下方式简化事物:
template<template<auto ...> class S, auto... U, auto... V>
struct match_class<S<U...>, S<V...>> : std::true_type{};
关于错误背后的原因, @barry的答案包含您需要理解的所有内容(像往常一样(。
这是一个通用的C 14解决方案,不依赖手动专业的类型特征或扩展Foo
和Bar
。
获取代表其参数类型类模板类型的模板元功能:
namespace detail
{
// Type representing a class template taking any number of non-type template arguments.
template <typename T, template <T...> class U>
struct nontype_template {};
}
// If T is an instantiation of a class template U taking non-type template arguments,
// this has a nested typedef "type" that is a detail::nontype_template representing U.
template <typename T>
struct nontype_template_of {};
// Partial specializations for all of the builtin integral types.
template <template <bool...> class T, bool... Vs>
struct nontype_template_of<T<Vs...>> { using type = detail::nontype_template<bool, T>; };
template <template <char...> class T, char... Vs>
struct nontype_template_of<T<Vs...>> { using type = detail::nontype_template<char, T>; };
template <template <signed char...> class T, signed char... Vs>
struct nontype_template_of<T<Vs...>> { using type = detail::nontype_template<signed char, T>; };
template <template <unsigned char...> class T, unsigned char... Vs>
struct nontype_template_of<T<Vs...>> { using type = detail::nontype_template<unsigned char, T>; };
template <template <short...> class T, short... Vs>
struct nontype_template_of<T<Vs...>> { using type = detail::nontype_template<short, T>; };
template <template <unsigned short...> class T, unsigned short... Vs>
struct nontype_template_of<T<Vs...>> { using type = detail::nontype_template<unsigned short, T>; };
template <template <int...> class T, int... Vs>
struct nontype_template_of<T<Vs...>> { using type = detail::nontype_template<int, T>; };
template <template <unsigned int...> class T, unsigned int... Vs>
struct nontype_template_of<T<Vs...>> { using type = detail::nontype_template<unsigned int, T>; };
template <template <long...> class T, long... Vs>
struct nontype_template_of<T<Vs...>> { using type = detail::nontype_template<long, T>; };
template <template <unsigned long...> class T, unsigned long... Vs>
struct nontype_template_of<T<Vs...>> { using type = detail::nontype_template<unsigned long, T>; };
template <template <long long...> class T, long long... Vs>
struct nontype_template_of<T<Vs...>> { using type = detail::nontype_template<long long, T>; };
template <template <unsigned long long...> class T, unsigned long long... Vs>
struct nontype_template_of<T<Vs...>> { using type = detail::nontype_template<unsigned long long, T>; };
易用使用的别名模板:
// Alias template for nontype_template_of.
template <typename T>
using nontype_template_of_t = typename nontype_template_of<T>::type;
然后,您可以像这样实现match_class
特征:
template <class A, class B>
struct match_class : std::is_same<nontype_template_of_t<A>, nontype_template_of_t<B>> {};
demo
- ArduinoJson 6.15.2:JsonObject没有命名类型
- 防止主数据类型C++的隐式转换
- 大量序列中核苷酸类型的快速计数
- 如何从C++中的依赖类型中获得它所依赖的类型
- 有关插入适配器的错误。[错误]请求从 'back_insert_iterator<vector<>>' 类型转换为非标量类型
- 是否可以初始化不可复制类型的成员变量(或基类)
- 如何获取std::result_of函数的返回类型
- 从父命名空间重载类型
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 我想将一个对T类型的非常量左值引用绑定到一个T类型的临时值
- Openssl 1.1.1d无效使用不完整的类型"struct dsa_st"
- 访问者访问变体并返回不同类型时出错
- 在VS2010-VS2015下编译时,如何使用decltype作为较大类型表达式的LHS
- 处理小于cpu数据总线的数据类型.(c++转换为机器代码)
- C++ 雷神库 - 使用资源加载器类时出现问题(不命名类型)
- 调用每种类型的变异模板的正确模板专业化
- 匹配的变异非类型模板
- 如何使模板通过变异参数推断返回类型的函数
- 带有变异模板的更多相关类型
- 变异变量与非变异类型的相互作用