检查参数包是否包含类型

发布于 2024-08-18 19:29:20 字数 341 浏览 9 评论 0原文

我想知道 C++0x 是否提供任何内置功能来检查可变参数模板的参数包是否包含特定类型。今天,如果您使用 boost:::mpl::vector 作为可变参数模板的替代品,则可以使用 boost:::mpl::contains 来完成此任务。然而,它具有严重的编译时间开销。我想,C++0x 对 std::is_same 具有编译器级支持。所以我在想编译器是否也支持像下面这样的泛化。

template <typename... Args, typename What>
struct is_present
{
  enum { value = (What in Args...)? 1 : 0 };
};

I was wondering if C++0x provides any built-in capabilities to check if a parameter pack of a variadic template contains a specific type. Today, boost:::mpl::contains can be used to accomplish this if you are using boost::mpl::vector as a substitute for variadic templates proper. However, it has serious compilation-time overhead. I suppose, C++0x has compiler-level support for std::is_same. So I was thinking if a generalization like below is also supported in the compiler.

template <typename... Args, typename What>
struct is_present
{
  enum { value = (What in Args...)? 1 : 0 };
};

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

风透绣罗衣 2024-08-25 19:29:20

幸运的是,C++ 标准已经发展。使用 C++1z 又名 C++17,您终于可以轻松地迭代参数包。因此,答案的代码(几乎)很简单,正如问题中所建议的:

template<typename What, typename ... Args>
struct is_present {
    static constexpr bool value {(std::is_same_v<What, Args> || ...)};
};

看起来怪异的 (std::is_same_v|| ...) 由编译器内部为 (std::is_same_v|| std::is_same_v|| ...),这正是,你想要什么。它甚至可以正确地生成带有空 Args 参数包的 false

甚至可以在函数或方法中内联执行整个检查 - 不再需要辅助结构:

template<typename T, typename ... List>
void foo(T t, List ... lst)
{
    if constexpr((std::is_same_v<T, List> || ...)) {
        std::cout << "T is in List" << std::endl;
    } else {
        std::cout << "T is not in List" << std::endl;
    }
}

注意:这取自 另一个问题,已标记为此问题的重复项。由于这是本主题的“规范”问题,因此我在此处添加了重要信息。

Fortunately, the C++ standard has evolved. With C++1z aka C++17, you can finally iterate easily over parameter packs. So the code for the answer is (almost) as simple, as suggested in the question:

template<typename What, typename ... Args>
struct is_present {
    static constexpr bool value {(std::is_same_v<What, Args> || ...)};
};

The weird-looking (std::is_same_v<What, Args> || ...) is expanded by the compiler internally to (std::is_same_v<What, Args[0]> || std::is_same_v<What, Args[1]> || ...), which is exactly, what you want. It even correctly yields false with an empty Args parameter pack.

It is even possible to do the whole check inline in a function or method - no helper structs are required anymore:

template<typename T, typename ... List>
void foo(T t, List ... lst)
{
    if constexpr((std::is_same_v<T, List> || ...)) {
        std::cout << "T is in List" << std::endl;
    } else {
        std::cout << "T is not in List" << std::endl;
    }
}

Note: This has been taken from another question, that was marked as a duplicate of this question. As this is the "canonical" question for this topic, I added that important information here.

樱花细雨 2024-08-25 19:29:20

不,您必须使用可变参数模板的(部分)专业化来进行如下编译时计算:

#include <type_traits>

template < typename Tp, typename... List >
struct contains : std::true_type {};

template < typename Tp, typename Head, typename... Rest >
struct contains<Tp, Head, Rest...>
: std::conditional< std::is_same<Tp, Head>::value,
    std::true_type,
    contains<Tp, Rest...>
>::type {};

template < typename Tp >
struct contains<Tp> : std::false_type {};

可变参数模板只有另一种内在操作,那就是 sizeof 运算符的特殊形式,它计算参数列表的长度例如:

template < typename... Types >
struct typelist_len
{
   const static size_t value = sizeof...(Types);
};

您从哪里得到“它具有严重的编译时间开销”和 boost mpl 的信息?我希望你不只是在这里做出假设。 Boost mpl 使用惰性模板实例化等技术来尝试减少编译时间,而不是像天真的模板元编程那样爆炸。

No, you have to use (partial) specialization with variadic templates to do compile-time computations like this:

#include <type_traits>

template < typename Tp, typename... List >
struct contains : std::true_type {};

template < typename Tp, typename Head, typename... Rest >
struct contains<Tp, Head, Rest...>
: std::conditional< std::is_same<Tp, Head>::value,
    std::true_type,
    contains<Tp, Rest...>
>::type {};

template < typename Tp >
struct contains<Tp> : std::false_type {};

There is only one other intrinsic operation for variadic templates and that is the special form of the sizeof operator which computes the length of the parameter list e.g.:

template < typename... Types >
struct typelist_len
{
   const static size_t value = sizeof...(Types);
};

Where are you getting "it has serious compilation-time overhead" with boost mpl from? I hope you are not just making assumptions here. Boost mpl uses techniques such as lazy template instantiation to try and reduce compile-times instead of exploding like naive template meta-programming does.

离线来电— 2024-08-25 19:29:20

如果您想避免手动类型递归,在我看来,std::common_type 是 STL 中唯一的实用程序,它是可变参数模板,因此也是唯一可能封装递归的实用程序。

解决方案 1

std::common_type 查找一组类型中派生最少的类型。如果我们识别具有类型的数字,特别是具有较少派生类型的高数字,它会找到集合中的最大数字。然后,我们必须将与键类型的相等性映射到派生级别。

using namespace std;

struct base_one { enum { value = 1 }; };
struct derived_zero : base_one { enum { value = 0 }; };

template< typename A, typename B >
struct type_equal {
 typedef derived_zero type;
};

template< typename A >
struct type_equal< A, A > {
 typedef base_one type;
};

template< typename Key, typename ... Types >
struct pack_any {
 enum { value =
     common_type< typename type_equal< Key, Types >::type ... >::type::value };
};

解决方案 2

我们可以对 common_type 进行更多修改。标准说

如果以下情况,程序可以专门化此特征:
中至少有一个模板参数
专业化是用户定义的类型。

并准确描述了其中的内容:递归部分特化情况、应用二元运算符的情况和终端情况。本质上,它是一个通用的 fold 函数,您可以添加任何您喜欢的二进制运算。这里我使用加法,因为它比 OR 提供更多信息。请注意,is_same 返回一个integral_constant

template< typename Addend >
struct type_sum { // need to define a dummy type to turn common_type into a sum
    typedef Addend type;
};

namespace std { // allowed to specialize this particular template
template< typename LHS, typename RHS >
struct common_type< type_sum< LHS >, type_sum< RHS > > {
    typedef type_sum< integral_constant< int,
     LHS::type::value + RHS::type::value > > type; // <= addition here
};
}

template< typename Key, typename ... Types >
struct pack_count : integral_constant< int,
 common_type< type_sum< is_same< Key, Types > > ... >::type::type::value > {};

If you want to avoid manual type recursion, std::common_type appears to me to be the only utility in the STL which is a variadic template, and hence the only one which could potentially encapsulate recursion.

Solution 1

std::common_type finds the least-derived type in a set of types. If we identify numbers with types, specifically high numbers with less-derived types, it finds the greatest number in a set. Then, we have to map equality to the key type onto a level of derivation.

using namespace std;

struct base_one { enum { value = 1 }; };
struct derived_zero : base_one { enum { value = 0 }; };

template< typename A, typename B >
struct type_equal {
 typedef derived_zero type;
};

template< typename A >
struct type_equal< A, A > {
 typedef base_one type;
};

template< typename Key, typename ... Types >
struct pack_any {
 enum { value =
     common_type< typename type_equal< Key, Types >::type ... >::type::value };
};

Solution 2

We can hack common_type a little more. The standard says

A program may specialize this trait if
at least one template parameter in the
specialization is a user-defined type.

and describes exactly what is inside it: a recursive partial specialization case, a case which applies a binary operator, and a terminal case. Essentially, it's a generic fold function, and you can add whatever binary operation you please. Here I used addition because it's more informative than OR. Note that is_same returns an integral_constant.

template< typename Addend >
struct type_sum { // need to define a dummy type to turn common_type into a sum
    typedef Addend type;
};

namespace std { // allowed to specialize this particular template
template< typename LHS, typename RHS >
struct common_type< type_sum< LHS >, type_sum< RHS > > {
    typedef type_sum< integral_constant< int,
     LHS::type::value + RHS::type::value > > type; // <= addition here
};
}

template< typename Key, typename ... Types >
struct pack_count : integral_constant< int,
 common_type< type_sum< is_same< Key, Types > > ... >::type::type::value > {};
書生途 2024-08-25 19:29:20

从 C++17 开始,您可以继承 std::disjunction 使用 std::is_same... 的正常包扩展,在所有 std::is_same。您的 is_present 类型特征将有一个 static constexpr bool value 成员变量来保存结果。这与 || 上的折叠表达式不同,因为如果找到匹配项,它会短路其余 is_same<>::value 的实例化。您可以在下面的 C++11/14 实现中看到它是如何工作的。

#include <type_traits>

template<class What, class... Args>
struct is_present : std::disjunction<std::is_same<What, Args>...> {};

使用 C++11 或 C++14,您可以使用 析取 "nofollow noreferrer">std::conditional 并在定义 is_present 时使用该类型特征而不是 std::disjunction

template<class...> struct disjunction : std::false_type {};
template<class T> struct disjunction<T> : T {};
template<class T, class... Ts>
struct disjunction<T, Ts...> :
    std::conditional<bool(T::value),
                     T, // no `is_same<>::value` instantiations for the Ts...
                     disjunction<Ts...>>::type {};

: C++14 您还可以创建辅助变量模板:

template<class... Ts>
constexpr bool is_present_v = is_present<Ts...>::value;
int main() {
    std::cout << is_present_v<int, double, int> << '\n'; // prints 1
}

Since C++17, you can inherit from std::disjunction with a normal pack expansion of std::is_same<What, Args>... which performs a logical OR between all the std::is_sames. Your is_present type trait will have a static constexpr bool value member variable holding the result. This is different from a fold expression over || since it's short-circuiting the instantiation of the rest of the is_same<>::values if a match is found. You can see how that works in the C++11/14 implementation below.

#include <type_traits>

template<class What, class... Args>
struct is_present : std::disjunction<std::is_same<What, Args>...> {};

Using C++11 or C++14, you can define your own disjunction using std::conditional and use that type trait instead of std::disjunction when defining is_present:

template<class...> struct disjunction : std::false_type {};
template<class T> struct disjunction<T> : T {};
template<class T, class... Ts>
struct disjunction<T, Ts...> :
    std::conditional<bool(T::value),
                     T, // no `is_same<>::value` instantiations for the Ts...
                     disjunction<Ts...>>::type {};

Since C++14 you can also create a helper variable template:

template<class... Ts>
constexpr bool is_present_v = is_present<Ts...>::value;
int main() {
    std::cout << is_present_v<int, double, int> << '\n'; // prints 1
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文