如何判断模板类型是否是模板类的实例?

Cam*_*owe 1 c++ templates type-traits

我有一个采用模板类型来确定返回值的函数。有什么方法可以在编译时判断模板类型是否是模板类的实例化?

例如

class First { /* ... */ };

template <typename T>
class Second { /* ... */ };

using MyType = boost::variant<First, Second<int>, Second<float>>;

template <typename SecondType>
auto func() -> MyType {
    static_assert(/* what goes here?? */, "func() expects Second type");
    SecondType obj;
    // ...
    return obj;
}

MyType obj = func<Second<int>>();
Run Code Online (Sandbox Code Playgroud)

我知道这样做可以解决这个问题

template <typename T>
auto func() -> MyType {
    static_assert(std::is_same<T, int>::value || std::is_same<T, float>::value,
                  "func template must be type int or float");

    Second<T> obj;
    // ...
    return obj;
}

MyType obj = func<int>();
Run Code Online (Sandbox Code Playgroud)

我只是很好奇总体上是否有一种方法可以测试类型是否是模板类的实例化?因为如果MyType最终有6个Second实例化,我不想测试所有可能的类型。

Fat*_*KIR 8

这是一个选择:

#include <iostream>
#include <type_traits>
#include <string>

template <class, template <class> class>
struct is_instance : public std::false_type {};

template <class T, template <class> class U>
struct is_instance<U<T>, U> : public std::true_type {};

template <class>
class Second 
{};

int main()
{
    using A = Second<int>;
    using B = Second<std::string>;
    using C = float;
    std::cout << is_instance<A, Second>{} << '\n'; // prints 1
    std::cout << is_instance<B, Second>{} << '\n'; // prints 1
    std::cout << is_instance<C, Second>{} << '\n'; // prints 0
}
Run Code Online (Sandbox Code Playgroud)

基本上is_instance,它是针对作为模板实例化的类型的结构进行专门化处理。

  • 最好使它为`template &lt;class,template &lt;class ...&gt; class&gt; struct is_instance`。这样,您就不会局限于仅使用一个参数的模板。还应注意,这仅适用于带有typename参数的模板。 (4认同)

Ric*_*ges 8

另一种选择,拿起亨利的评论:

#include <iostream>
#include <type_traits>
#include <string>

template <class, template <class, class...> class>
struct is_instance : public std::false_type {};

template <class...Ts, template <class, class...> class U>
struct is_instance<U<Ts...>, U> : public std::true_type {};

template <class>
class Second 
{};

template <class, class, class>
class Third 
{};

int main()
{
    using A = Second<int>;
    using B = Second<std::string>;
    using C = float;
    using D = Third<std::string, int, void>;
    std::cout << is_instance<A, Second>{} << '\n'; // prints 1
    std::cout << is_instance<B, Second>{} << '\n'; // prints 1
    std::cout << is_instance<C, Second>{} << '\n'; // prints 0
    std::cout << is_instance<D, Third>{} << '\n'; // prints 1
}
Run Code Online (Sandbox Code Playgroud)

  • @user3520616问题是3是一个整数,它是一个非类型模板参数。我们必须将其提升为一种类型(例如 std::integral_constant)。这意味着为 std::array 提供专门化,或增加 is_instance 的复杂性。我会考虑一下。 (2认同)

dav*_*igh 7

对 @RichardHodges 答案的另一项改进:通常,人们不仅希望捕获普通类型,还希望捕获所有cv 限定ref 限定类型。

换句话说,如果is_instance<A, Second>{}是真的,那么也is_instance<A const&, Second>{}或者is_instance<A&&, Second>{}应该是真的。该线程中的当前实现不支持这一点。

以下代码通过添加另一个间接寻址和 a 来说明所有 cv-ref 限定类型std::remove_cvref_t

namespace
{
    template <typename, template <typename...> typename>
    struct is_instance_impl : public std::false_type {};

    template <template <typename...> typename U, typename...Ts>
    struct is_instance_impl<U<Ts...>, U> : public std::true_type {};
}

template <typename T, template <typename ...> typename U>
using is_instance = is_instance_impl<std::remove_cvref_t<T>, U>;
Run Code Online (Sandbox Code Playgroud)

使用它作为

#include <iostream>

template <typename ...> struct foo{};
template <typename ...> struct bar{};

int main()
{
    std::cout << is_instance<foo<int>, foo>{} << std::endl;           // prints 1
    std::cout << is_instance<foo<float> const&, foo>{} <<std::endl;   // prints 1
    std::cout << is_instance<foo<double,int> &&, foo>{} << std::endl; // prints 1
    std::cout << is_instance<bar<int> &&, foo>{} << std::endl;        // prints 0
}
Run Code Online (Sandbox Code Playgroud)