Mat*_*szL 4 c++ templates variadic-functions variadic-templates c++14
我正在编写由单一类型参数化的模板函数,并且具有可变数量的相同类型(不是不同类型)的参数。它应该检查第一个值是否在其余值中。我想这样写:
#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;
}
Run Code Online (Sandbox Code Playgroud)
我希望第二个工作,但它不编译,因为
test.cpp: In function ‘bool value_in(T, T, ...)’:
test.cpp:7:46: error: expansion pattern ‘vals’ contains no argument packs
Run Code Online (Sandbox Code Playgroud)
我看到了“ (T, T, ...)”而不是“ (T, T...)”,所以可能我搞砸了函数声明并以 C 风格的可变参数函数结束。
如何编写接受任意数量的相同类型参数的声明?
首先,定义一个C风格的可变参数函数
static bool value_in (T val, T vals, ...)
Run Code Online (Sandbox Code Playgroud)
前面的逗号...是可选的。
所以你的
static bool value_in(T val, T vals...)
Run Code Online (Sandbox Code Playgroud)
定义两个非可变参数(val和vals)和一个未命名的可变参数序列。
如何编写接受任意数量的相同类型参数的声明?
有很多方法但是,恕我直言,有缺点
一种可能的方法是使用 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();
}
Run Code Online (Sandbox Code Playgroud)
您也可以在 C++11/C++14 中开发此解决方案,但稍微复杂一些。
缺点:Ts...类型是推导出来的,必须是完全相同的T类型。
因此,例如,如果您想要一个接受 列表的函数,std::string()则不能使用char const *
value_in(std::string{"abc"}, "123");
Run Code Online (Sandbox Code Playgroud)
因为T, std::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;
Run Code Online (Sandbox Code Playgroud)
现在您可以编写value_in()拦截所有值的第一步,检测所有类型(无限制)并将它们传递给第二步函数,如下所示
template <typename T, typename ... Ts>
bool value_in (T val, Ts ... vals)
{ return value_in_helper<T, Ts...>(val, vals...); }
Run Code Online (Sandbox Code Playgroud)
第二步函数改变所有Ts...类型中T使用firstType
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();
}
Run Code Online (Sandbox Code Playgroud)
此解决方案与 C++11 兼容。
缺点:你需要第二步。
优势(恕我直言):此解决方案通过第二步函数,该函数声明为接收T类型,因此也接受可转换为T.
也就是说:这个解决方案也接受
value_in(std::string{"abc"}, "123");
Run Code Online (Sandbox Code Playgroud)
因为不再需要"123"完全是std::string; 也可以转换为std::string.