【问题标题】:How to detect if a type is one of a list of generic types如何检测类型是否是泛型类型列表之一
【发布时间】:2021-05-30 23:38:03
【问题描述】:

如果我有

template <typename T> struct A;
template <typename T> struct B;
template <typename T> struct C;
template <typename T> struct D;

如果某个候选类型X 是其中之一,那么最紧凑的测试方法是什么?我正在寻找类似的东西

boost::enable_if< is_instantiation_of_any<X,A,B,C,D> >

但是 A、B、C 和 D 是模板,所以我不确定如何构建上述内容。

【问题讨论】:

  • X,A,B,C,D 是模板列表,而不是类型列表。你想要模板,对吧? (即它是std::is_instantiation_of_any 而不是is_any
  • 是的,这个名字更合适。
  • @bradgonesurfing,C++11 是您正在寻找的最高标准吗?如果没有,我已经发布了 C++14/17 的答案,您可以在问题中添加另一个标签。
  • 是的,它是客户 API 的一部分,目前我们的限制是 C++11。它仍然是一个有用的答案,所以把它留在那里,但这个问题也得到了 C++11 答案的回答。

标签: c++ c++11 template-meta-programming enable-if


【解决方案1】:

不确定是否存在std::is_instantiation_of,但如果所有模板都具有相同数量的参数,则实现它很简单(如果不存在则更复杂)。要检查一个类型是否是任何给定模板的实例化,您只需将其折叠(需要 C++17):

#include<iostream>
#include<type_traits>


template <typename T> struct A;
template <typename T> struct B;
template <typename T> struct C;
template <typename T> struct D;


template <typename T,template<typename> typename X>
struct is_instantiation_of : std::false_type {};

template <typename A,template<typename> typename X>
struct is_instantiation_of<X<A>,X> : std::true_type {};

template <typename T,template<typename> typename...X>
struct is_instantiation_of_any {
    static const bool value = ( ... || is_instantiation_of<T,X>::value);
};

int main(){
    std::cout << is_instantiation_of< A<int>, A>::value;
    std::cout << is_instantiation_of< A<double>, B>::value;
    std::cout << is_instantiation_of_any< A<int>,A,B>::value;
}

Output:

101

要获得符合 C++11 的解决方案,我们可以使用 Jarod42s answers 中的这个巧妙技巧:

template <bool ... Bs>
using meta_bool_and = std::is_same<std::integer_sequence<bool, true, Bs...>,
                                   std::integer_sequence<bool, Bs..., true>>;

它相当聪明,true,a,b,ca,b,c,true 只有在 abc 都为 true 时才相同。 std::integer_sequence 是 C++14,但我们需要的只是一个在其定义中包含 bool 的类型:

namespace my {
    template <typename T,T ...t>
    struct integer_sequence {};
}

使用它我们可以将上面的内容重写为:

template <bool ... Bs>
using my_all = std::is_same<my::integer_sequence<bool, true, Bs...>,
                            my::integer_sequence<bool, Bs..., true>>;

因为"ANY(a,b,c,d,...)" 就是"! ALL( !a, !b, !c, !d,...)",我们可以使用:

template <bool ... Bs>
struct my_any { static constexpr bool value = ! my_all< ! Bs...>::value; };

以 C++11 友好的方式编写 is_instantiation_of_any

template <typename T,template<typename> typename...X>
struct is_instantiation_of_any {
    static const bool value = my_any< is_instantiation_of<T,X>::value ...>::value;
};

Complete C++11 example

【讨论】:

  • 现在让它适用于具有任意数量的类型和非类型参数的模板:D
  • @AyxanHaqverdili 请不要逗我,我的休息时间已经太长了 ;)
  • 这些模板问题也经常狙击我xkcd.com/356
  • 标记为 C++11,因此应替换折叠表达式。 (有一个trick for all_of 以避免递归,any_of 只是none_of 的否定(即all_of 的否定条件)。
  • @bradgonesurfing 不确定 Ayxans 的评论有多严重,问题是“任意数量的混合类型和非类型参数”不仅比我在回答中提出的复杂 x2 或 x3,而且这是一个完全不同的故事。
【解决方案2】:

使用 C++17(或 C++14,如果您根据 cmets 进行编辑),您可以使用 boost::hana::any_of 作为助手:

#include <iostream>
#include <boost/hana/any_of.hpp>
#include <boost/hana/tuple.hpp>
#include <type_traits>

namespace hana = boost::hana;

template <typename T> struct A {};
template <typename T> struct B {};
template <typename T> struct C {};
template <typename T> struct D {};
template <typename T> struct N {};

template<typename T>
bool fun(T) {      // overload for non-templated arguments (templated arguments
    return false;  // are a better match for the overload below)
}

template<typename T, template<typename> typename X>
bool fun(X<T>) { // overload for templated arguments
    auto constexpr pred = [](auto x){ // remove constexpr for < c++17
        return std::is_same_v<decltype(x), X<T>>;
        // return std::is_same<decltype(x), X<T>>::value; // for < c++17
    };
    return hana::any_of(hana::tuple<A<T>, B<T>, C<T>, D<T>>{}, pred);
};

int main() {
    A<int> a{};
    N<int> b{};
    int x = 3; fun(x);
    std::cout << fun(a) << fun(b) << fun(x) << std::endl; // prints 100
}

您可能想在将内容传递给std::is_same_v 之前使用std::decay_t

【讨论】:

  • 这很有趣,但如果我理解正确,它会失败,例如int x; fun(x);,不是吗?换句话说,它只适用于具有单个参数的模板实例化类型
  • @largest_prime_is_463035818,是的,在这种情况下它会失败。但是,如果要检查变量是否属于非模板类(或模板类的特定实例),那么std::is_same_v 可以完成这项工作,不是吗?
  • 是的std::is_same_v 可以提供帮助,但据我了解,这是问题的一部分。 OP 想要测试任何类型 X 是否是 ABCD 的实例化
  • @largest_prime_is_463035818,哦,好的,我现在明白你的意思了。嗯,它很快。如果它是正确的:P
  • 是的。我不确定,但我认为您只需要添加一个返回 false 的重载
猜你喜欢
  • 2014-11-06
  • 2014-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-24
  • 2020-06-07
  • 2019-06-25
  • 2016-04-14
相关资源
最近更新 更多