C++ 概念:检查模板实例化

C++ Concepts: checking for template instantiation

假设我有一个模板化类型,例如

template<typename A, typename B, typename C>
struct mytype { };

如何编写一个概念来检查类型是否是该模板的实例化?

template<typename T>
concept MyType = requires(T x) { ??? }

如果不解决旧式专用检测器类型或标记基类型,我想不出一个明显的方法。

您可以为此目的定义自己的元函数(类型特征):

template <typename T>
struct is_mytype : std::false_type { };

template <typename A, typename B, typename C>
struct is_mytype<mytype<A, B, C>> : std::true_type { };

template <typename T>
concept MyType = is_mytype<T>::value;

但说实话,我不知道是否有一种方法可以直接定义这样一个概念而不需要单独的元函数。

使用 C++17 class 模板参数推导,你应该能够做这样的事情:

template<typename A, typename B, typename C>
struct mytype { };

template<class T>
concept C1 = requires(T x) { 
    { mytype{x} } -> std::same_as<T>;
};

mytype{x} 使用 class 模板参数推导来推导 ABC,所以如果你可以构造一个 mytype<A, B, C> 来自 T。特别是,如果 mytype 是可复制构造的,这是有效的,因为你有一个隐式声明的 copy-deduction guide 类似于:

template <typename A, typename B, typename C>
mytype(mytype<A, B, C>) -> mytype<A, B, C>;

检查 T 是否也是构造的 mytype 实例化避免匹配其他推导指南,例如,这将匹配任何没有 -> std::same_as<T> 的类型:

template <class A, class B, class C>
struct mytype {
    mytype(A);
};

template <class A>
mytype(A) -> mytype<A, A, A>;

建议的解决方案不适用于不可复制构造的 classes,尽管应该可以使其适用于仅移动 classes。


测试 and : https://godbolt.org/z/ojdcrYqKv

您可以编写通用特征来检查专业化:

template <typename T, template <typename...> class Z>
struct is_specialization_of : std::false_type {};

template <typename... Args, template <typename...> class Z>
struct is_specialization_of<Z<Args...>, Z> : std::true_type {};

template <typename T, template <typename...> class Z>
inline constexpr bool is_specialization_of_v = is_specialization_of<T,Z>::value;

你可以把它变成一个广义的概念:

template<typename T, template <typename...> class Z>
concept Specializes = is_specialization_of_v<T, Z>;

template<typename T>
concept MyType = Specializes<T, mytype>;

或者只是一个专门的:

template<typename T>
concept MyType = is_specialization_of_v<T, mytype>;

如果您给模板 class 一些特征,您可以执行以下操作:

template<typename A, typename B, typename C>
struct mytype {
    using a_type = A;
    using b_type = B;
    using c_type = C;
};

与关联概念:

template <typename T>
concept is_mytype =
    std::is_same_v<
        std::remove_const_t<T>,
        mytype<typename T::a_type, typename T::b_type, typename T::c_type>>;

或者,如果 mytype 具有这些类型的成员,您可以跳过特征:

template<typename A, typename B, typename C>
struct mytype {
    A a_inst;
    B b_inst;
    C c_inst;
};

给出概念:

template <typename T>
concept is_mytype =
    std::is_same_v<
        std::remove_const_t<T>,
        mytype<decltype(T::a_inst), decltype(T::b_inst), decltype(T::c_inst)>>;

为了简洁起见:

template<typename T>
concept MyType = requires(T** x) {
    []<typename A, typename B, typename C>(mytype<A, B, C>**){}(x);
};

双指针对于避免派生到基础的转换是必要的,例如。 struct S : mytype<int, int, int> {}.

目前这在 clang 中不起作用,因为它不允许在未计算的上下文中使用 lambda。您可以通过提供辅助变量模板来解决此问题:

template<class T> constexpr auto L = []<typename A, typename B, typename C>(mytype<A, B, C>**){};
template<typename T>
concept MyType = requires(T** x) { L<T>(x); };

只要 mytype 的模板参数都是类型,您可以使用占位符使其更简洁:

template<typename T>
concept MyType = requires(T** x) { [](mytype<auto, auto, auto>**){}(x); };

目前,这只适用于gcc。