C++ 非类型参数包扩展

标签 c++ templates c++14 variadic-templates variadic-functions

我正在编写由单一类型参数化的模板函数,并且具有可变数量的相同类型(不是不同类型)的参数。它应该检查第一个值是否在其余值中。我想这样写:

#include <unordered_set>

template <typename T>
static bool value_in(T val, T vals...) {
    // compiles, but uses only vals[0]:
    const std::unordered_set<T> allowed {vals};
    // error: pack expansion does not contain any unexpanded parameter packs:
    // const std::unordered_set<T> allowed {vals...};
    return allowed.find(val) != allowed.end();
}
// usage
enum class Enumeration {one, two, three};
int main () {
    // should return true -> 0
    return value_in(Enumeration::two,
                    Enumeration::one,
                    Enumeration::two) ? 0 : 1;
}

我希望第二个能工作,但它没有编译,因为

test.cpp: In function ‘bool value_in(T, T, ...)’:
test.cpp:7:46: error: expansion pattern ‘vals’ contains no argument packs

我看到的是“(T, T, ...)”而不是“(T, T...)”,所以我可能搞砸了函数声明并以 C 风格的可变参数函数结束。

如何编写接受任意数量的相同类型参数的声明?

最佳答案

首先,定义一个C风格的可变参数函数

static bool value_in (T val, T vals, ...)

... 之前的逗号是可选的。

所以你的

static bool value_in(T val, T vals...)

定义两个非可变参数(valvals)和一个未命名的可变参数序列。

How to write declaration that will accept arbitrary number of parameters of the same type?

有很多方法,但是,恕我直言,有缺点

一种可能的方法是使用 SFINAE:您可以强制可变参数类型等于第一种类型。

以下是使用模板折叠的C++17可能的解决方案

template <typename T, typename ... Ts>
std::enable_if_t<(std::is_same<T, Ts>::value && ...), bool>
   value_in (T val, Ts ... vals) 
 {
   const std::unordered_set<T> allowed {val, vals ... };

   return allowed.find(val) != allowed.end();
 }

您也可以在 C++11/C++14 中开发此解决方案,但稍微复杂一些。

缺点:Ts... 类型被推导,它们必须是完全相同的T 类型。

因此,例如,如果您想要一个接受 std::string() 列表的函数,则不能使用 char const * 调用它

value_in(std::string{"abc"}, "123");

因为Tstd::string,不同于Ts...char const * , 并且 SFINAE 不启用 value_in

您可以使用 std::is_convertible 而不是 std::is_same 但我建议另一种方法,分两步。

首先,您需要一个自定义类型特征(使用 using 帮助程序)从列表中选择第一个类型

template <typename T, typename ...>
struct firstType
 { using type = T; };

template <typename T, typename ... Ts>
using firstType_t = typename firstType<T, Ts...>::type;

现在您可以编写第一步 value_in() 拦截所有值,检测所有类型(无限制)并将它们传递给第二步函数,如下所示

template <typename T, typename ... Ts>
bool value_in (T val, Ts ... vals) 
 { return value_in_helper<T, Ts...>(val, vals...); }

第二步函数使用firstType更改T中的所有Ts...类型

template <typename T, typename ... Ts>
bool value_in_helper (T val, firstType_t<T, Ts> ... vals) 
 {
   const std::unordered_set<T> allowed {val, vals ... };

   return allowed.find(val) != allowed.end();
 }

此解决方案与 C++11 兼容。

缺点:你需要第二步。

优点(恕我直言):此解决方案通过第二个步骤函数,该函数声明接收 T 类型,因此也接受可转换为 T 的参数。

即:这个解也接受

value_in(std::string{"abc"}, "123");

因为不再需要 "123" 正好是一个 std::string;也可以转换为 std::string

关于C++ 非类型参数包扩展,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51593695/

相关文章:

C++14:你能在 constexpr 中调用 new 吗?

c++ - C++中结构的输入元素

c++ - GCC 标准库 XCode 4.5

c++ - 有没有办法使用基于范围的 for 循环迭代最多 N 个元素?

c++ - 构造函数重载和 SFINAE

wpf - 在 WPF : how to disable the animation when the button after be pressed? 中

c++ - vector<T>::iterator - 无效?

c++ - FBO分离纹理

C# IteropServices 与 C++ dll - 在 excel 中工作?

c++ - 无法将 'const pointer const' 传递给 const ref