c++非类型参数包扩展
c++ non-type parameter pack expansion
我正在编写一个由单一类型参数化的模板函数,该函数具有不同数量的相同类型(而不是不同类型(的参数。它应该检查第一个值是否在其他值中。我想这样写:
#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, ...)
CCD_ 3之前的逗号是可选的。
所以你的
static bool value_in(T val, T vals...)
定义两个非可变参数(val
和vals
(和一个未命名的可变序列。
如何编写接受任意数量相同类型参数的声明?
有很多方法,但IMHO有缺点
一种可能的方法是使用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");
因为T
、std::string
不同于Ts...
、char const *
,并且SFINAE不启用value_in
。
您可以使用std::is_convertible
而不是std::is_same
,但我建议使用另一种方法,分两步。
首先,您需要一个自定义类型traits(使用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兼容。
缺点:你需要第二步。
优点(IMHO(:此解决方案通过第二步函数,该函数声明接收T
类型,因此也接受可转换为T
的参数。
也就是说:这个解决方案也接受
value_in(std::string{"abc"}, "123");
因为不再需要CCD_ 24恰好是CCD_;也可以转换为CCD_ 26。
我在这里看到了两个选项。您可以传递std::initializer_list
,这将导致函数签名更改为
#include <initializer_list>
template <typename T>
static bool value_in(T&& val, std::initializer_list<T> vals)
{
/* Implementation as before */
}
以及的调用片段
return value_in(Enumeration::two,
{ Enumeration::one, Enumeration::two }) ? 0 : 1;
注意这里的附加大括号,它们是构造要传递的初始值设定项列表所必需的。这种方法的一个小细节是函数签名,它立即表明只有一种类型可以推导。
如果你觉得打牙套不对,坚持你最初的尝试,调整你的功能,这样
template <typename S, typename... T>
static bool value_in(S&& val, T&&... vals) {
const std::unordered_set<S> allowed {std::forward<T>(vals)...};
/* As before... */
}
这允许像在原始代码段中那样调用函数。与上面的解决方案相反,这个签名显然有两个模板参数,这可能需要再次查看,如果S
与T
不同,它将失败。
- 嵌套参数包扩展失败
- [temp.variadic]中关于包扩展实例化的措辞
- 模板参数部分中有关包扩展的一些混淆
- 可变参数函数参数包扩展
- c++非类型参数包扩展
- C++别名的模板参数包扩展
- 如何将参数包扩展为向量<any>
- 模板包扩展以将函数应用于连续的参数对
- C++参数包扩展
- 如何为原生UI工具包扩展Ranorex?
- 折叠表达式、参数包扩展、类成员函数中的递归
- "如果 constexpr",在 lambda 内部,在包扩展内部 - 编译器错误?
- 折叠表达式扩展包中的最大元素数
- GCC vs CLANG:将捕获的参数包扩展两次
- 未使用模板类型定义的同时参数包扩展错误
- 构造仪的类模板参数包扩展
- 使用参数包扩展生成constexpr
- 如何"duplicate"模板参数包扩展?
- 模板.参数包扩展 - 重映射类型
- 哪个编译器(如果有的话)在参数包扩展中存在错误