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

4

我是一个新手,想学习可变模板,为了学习考虑以下函数:

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),但没有成功......

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


不确定,但你尝试过使用 typeid 函数了吗? - seleciii44
你希望 args 的类型 彼此相同,并且可能与 T 不同吗?或者你希望它们都与 T 相同的类型?或者你允许它们彼此不同,但只要能够使用可以转换为 T 的类型构建 T 数组即可? - Aaron McDaid
3个回答

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...);

编译器将会在类型不同的情况下抛出一个错误。
对于第二点,你可以修改allsame函数以返回这个类型。这个函数的唯一缺陷是如果该类型不能默认构造则无法工作。
template<typename T>
T allsame(T) { return{}; }

T allsame(T arg, T2 arg2, Ts... args)

然后,你可以使用decltype(allsame(args...))来获取类型。


3

首先,您需要这些头文件:

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

这确实可以工作,但我不理解你回答中的最后一部分,即元组部分...如果您能简要地说明一下,我将不胜感激...无论如何+1。 - Laith
1
@Leo std::tuple是一个标准的可变参数模板,类似于std::pair,但可以包含任意类型的列表。还有一个std::tuple_element辅助模板,可以用于通过索引访问元组字段或元组字段的类型。在我们的情况下,std::tuple<args.. >表示模板类型的列表,我们使用std::tuple_element<...>::type来获取索引为0的类型。由于模板参数具有相同的类型,我们可以使用其中任何一个作为类型来创建指针以保存数组。 - user2807083

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,以避免如果您的类型很大可能会出现的拷贝。
  • 第一个参数的类型是通过创建一个 std::tuple 类型并使用 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 会捕获它们!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接