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

How to check the type of passed arguments to variadic function

我是可变参数模板的新手,为了学习考虑以下函数

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 修改为 return。此函数的唯一缺点是如果类型不是默认可构造的,它将无法工作。

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

最后,让我们将函数的 return 类型作为第一类参数 - 如果所有类型都相同,我们可以采用其中任何一个。我们为此使用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 函数开始检查所有布尔值是否为真。这在检查所有 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,如果您的类型很大,可以避免潜在的副本。
  • 提取第一个参数的类型by creating a std::tuple type and using std::tuple_element<0, ..>
  • 使用
  • a static_assert,其中每种类型与第一种类型进行比较(通过 is_same)。
  • 我猜你想让 SFINAE 'hide' 这个函数,除非类型都相同。这是通过 typename = std::enable_if_t< ..boolean-expression.. >
  • 实现的
  • class ... 本质上是多余的。它的唯一目的是使开发人员无法通过手动指定类型(make_arr<int,char,size_t,bool>(..))来欺骗检查。然而,也许这太保守了——static_assert 无论如何都会抓住他们!