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

时间:2016-05-15 08:33:13

标签: c++ templates variadic

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

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),但它没有用......
  3. 注意:如果不合适,请编辑标题问题...谢谢

3 个答案:

答案 0 :(得分:4)

我发现的唯一方法是使用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...))获取类型

答案 1 :(得分:2)

首先,您需要以下内容:

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

答案 2 :(得分:2)

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, ..>
  • 使用static_assert,其中每种类型与第一种类型进行比较(通过is_same)。
  • 我想你希望SFINAE“隐藏”这个功能,除非这些类型完全相同。这是通过typename = std::enable_if_t< ..boolean-expression.. >
  • 实现的
  • class ...基本上是多余的。它的唯一目的是让开发人员无法通过手动指定类型(make_arr<int,char,size_t,bool>(..))来欺骗检查。但是,也许这太保守了 - static_assert无论如何都会抓住它们!