如何检查传递给varadic函数的参数的类型

How to check the type of passed arguments to variadic function

本文关键字:varadic 函数 参数 类型 何检查 检查      更新时间:2023-10-16

我对可变模板很陌生,为了学习,请考虑以下函数

template <typename T, typename... args>
T* make_arr(args... arg) {
   // Code to check if passed args are of the same type
   T* arr = new T[sizeof...(arg)]{ arg... };
   return arr;
}

我有两个问题:

  1. 我希望函数是模板化的,并且我希望传递的参数是相同类型的,所以问题是:是否可以检查传递的参数是否是相同类型
  2. 是否可以通过推导args...的类型来推导数组指针的类型,我的意思是不使用<typename T>。。。我使用了decltype(arg),但它不起作用

注意:如果标题问题不合适,请编辑。。。感谢

我找到的唯一方法是使用SFINAE 制作一个助手函数

//Basic function
template<typename T>
void allsame(T) {}
//Recursive function
template<typename T, typename T2, typename... Ts, 
typename = std::enable_if_t<std::is_same<T, T2>::value>>
void allsame(T arg, T2 arg2, Ts... args)
{
    allsame(arg2, args...);
}

然后你可以这样称呼它:

allsame(arg...);

如果类型不相同,编译器将抛出一个错误。


对于2),您可以修改allsame以返回类型。这个函数唯一的缺点是,如果类型不是默认可构造的,它就不起作用。

template<typename T>
T allsame(T) { return{}; }
T allsame(T arg, T2 arg2, Ts... args)

然后,您可以decltype(allsame(args...))来获得类型

首先,您需要这些包括:

#include <type_traits>
#include <tuple>

然后,让我们声明可变模板来检测类型是否相同:

template <typename ... args>
struct all_same : public std::false_type {};

template <typename T>
struct all_same<T> : public std::true_type {};

template <typename T, typename ... args>
struct all_same<T, T, args...> : public all_same<T, args ... > {};

现在我们可以使用static_assert来检测参数类型是否相同:

template <typename T, typename... args>
T* make_arr(args... arg) {
   // Code to check if passed args are of the same type
   static_assert(all_same<args ...>::value, "Params must have same types");
   T* arr = new T[sizeof...(arg)]{ arg... };
   return arr;
};

最后,让我们把函数的返回类型作为第一类参数——如果所有类型都相同,我们可以取其中的任何一个。我们使用std::tuple进行

template <typename... args>
typename std::tuple_element<0, std::tuple<args...> >::type * make_arr(args... arg) {
   // Code to check if passed args are of the same type
   static_assert(all_same<args ...>::value, "Params must have same types");
   typedef typename std::tuple_element<0, std::tuple<args...> >::type T;
   T* arr = new T[sizeof...(arg)]{ arg... };
   return arr;
};

constexpr bool函数开始,检查所有布尔值是否为true。当检查所有is_same调用是否为true时,这将非常有用。

constexpr bool all() {
    return true;
}
template<typename ...B>
constexpr bool all(bool b, B... bs) {
    return b && all(bs...);
}

总之,这里是make_arr函数:

template <typename... args
, class ...
, typename T = std::tuple_element_t<0, std::tuple<args...>>
, typename = std::enable_if_t< all(std::is_same<T, args>{}...) >
>
T* make_arr(args&&... arg) {
    static_assert( all(std::is_same<T, args>{}...) ,"");
    T* arr = new T[sizeof...(arg)]{ std::forward<args>(arg)... };
    return arr;
}

一些评论:

  • 使用完美的转发&&std::forward,以避免在您的类型较大时出现潜在的副本
  • 通过创建CCD_ 13类型并使用CCD_
  • 使用static_assert,其中将每种类型与第一种类型进行比较(经由is_same
  • 我想您希望SFINAE"隐藏"此函数,除非类型完全相同。这是通过typename = std::enable_if_t< ..boolean-expression.. >实现的
  • CCD_ 18基本上是冗余的。它的唯一目的是让开发人员不可能通过手动指定类型(make_arr<int,char,size_t,bool>(..))来欺骗检查。然而,也许这太保守了——static_assert无论如何都会抓住他们