【问题标题】:C++ Concepts: checking for template instantiationC++ 概念:检查模板实例化
【发布时间】:2019-01-14 13:12:41
【问题描述】:

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

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

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

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

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

【问题讨论】:

  • 不确定您到底在问什么。概念还没有在标准中,还是我错过了?
  • 它们在草稿 C++20 中,所以我的问题当然是基于当前草稿
  • 这个有什么用?
  • @cpplearner 这样的用例之一可能是仅对特定类型的参数进行完美转发。比 SFINAE 更好的概念 :)
  • @cpplearner 直接而明显的用途是强制类型安全,而不必经历通常的冗长模板舞蹈。也就是说,您可以直接使用template&lt;MyType T&gt; do_stuff(T) 而不是template&lt;typename A, typename B, typename C&gt; void do_stuff(mytype&lt;A, B, C&gt;)。希望有一天do_stuff(MyType T),正如 Stroustrup 所设想的那样。

标签: c++ c++-concepts c++20


【解决方案1】:

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

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;

但说实话,我不知道有没有办法直接定义这样一个概念而不需要单独的元函数。

【讨论】:

  • 是的,这也是我的解决方案。我希望这些概念能让这类探测器过时。
  • @MrMobster 它确实......一旦你写了它们。呵呵。
【解决方案2】:

使用C++17类模板参数推导,你应该可以做这样的事情:

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} 使用类模板参数推导推导ABC,因此如果您可以从T 构造mytype&lt;A, B, C&gt;,则这是有效的。特别是,如果 mytype 是可复制构造的,这是有效的,因为您有一个隐式声明的 copy-deduction guide,类似于:

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

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

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

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

建议的解决方案不适用于不可复制的类,即使应该可以使其适用于只能移动的类。


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

【讨论】:

  • 但谁能说A、B、C可以这样推演?
  • @einpoklum 总是有一个隐式生成的copy-deduction guide。即使使用(隐式)删除的复制构造函数,本指南也存在,但在这种情况下,该概念将失败(在我的回答中指定)。
  • 你的意思是,一个推导指南可以准确地推导出作为模板实例化的类型 T 的 A、B 和 C?
  • 我不知道为什么有人会拒绝你的回答——它很准确,准确地回答了我的问题。
  • @einpoklum 假设mytype 有一个未删除的复制构造函数,您有一个隐式生成的演绎指南,类似于:template &lt;class A, class B, class C&gt; mystruct(mystruct&lt;A, B, C&gt; const&amp;) -&gt; mystruct&lt;A, B, C&gt;;,这是复制演绎指南。
【解决方案3】:

您可以编写一个泛化特征来检查特化:

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

【讨论】:

  • 它并不是真正通用的,因为它只适用于本身采用类型的模板类。所以你不能问它是否是std::array 甚至std::span 的特化,因为它们采用非类型模板参数。
  • @NicolBolas 绝大多数模板只接受类型参数。所以我会说它很笼统。但是,是的,它不适用于这两个。
【解决方案4】:

为了简洁:

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

双指针对于避免派生到基址的转换是必要的,例如。 struct S : mytype&lt;int, int, int&gt; {}.

目前这在 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。

【讨论】:

  • 太棒了,谢谢!我使用了最后一个,但只有单个指针,因为我希望 能够检测模板化类型的派生类。这减少了 14 行代码。 \o/
  • 函数参数类型的模板参数中的auto 似乎实际上不是标准的 C++20,尽管 GCC 默默地允许它:-( 虽然 Clang 不允许,但 this other thread 表示这是仅在概念 TS 中,但不是有效的标准 C++20。那么对于 GCC 的 -std=c++20 标志来说就这么多了。:-/ 我已经重写了没有 auto
【解决方案5】:

如果你给你的模板类一些特征,你可以做以下事情:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-18
    • 2022-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多