c++ - 使用新的 c++14 / c++17 功能改进可变参数模板函数
问题描述
我是可变参数模板的新手,我仍然设法使用它在 c++11 中编写了一些代码,但我仍然对结果感到不满,因为它缺乏表现力。
问题是实现一个函数,该函数接受多个bool
条件(从到任何条件)并返回一个整数代码,指示第一个“ ”参数1
在什么位置,或者它们是否都是.false
0
true
e.g. "error_code(true, true, true);" must return 0
e.g. "error_code(true, true, false);" must return 3
e.g. "error_code(true, false, false);" must return 2
e.g. "error_code(false, false, false);" must return 1
我当前的代码代表(coliru 的实时链接:http://coliru.stacked-crooked.com/a/1b557f2819ae9775 ):
#include <tuple>
#include <iostream>
int error_impl(bool cond)
{
return cond;
}
template<typename... Args>
int error_impl(bool cond, Args... args)
{
return cond ? 1 + error_impl(args...) : 0;
}
template<typename... Args>
int error_code(Args... args)
{
constexpr std::size_t size = std::tuple_size<std::tuple<Args...>>::value + 1;
return (error_impl(args...) + 1) % size;
}
int main()
{
auto e1 = error_code(true, true, true);
auto e2 = error_code(true, true, false);
auto e3 = error_code(true, false, false);
auto e4 = error_code(false, false, false);
std::cout << std::boolalpha;
std::cout << "(true, true, true)==0 -> " << (e1 == 0) << "\n";
std::cout << "(true, true, false)==3 -> " << (e2 == 3) << "\n";
std::cout << "(true, false, false)==2 -> " << (e3 == 2)<< "\n";
std::cout << "(false, false, false)==1 -> " << (e4 == 1)<< "\n";
auto e5 = error_code(true, true, true, true);
auto e6 = error_code(true, true, true, false);
auto e7 = error_code(true, true, false, false);
auto e8 = error_code(true, false, false, false);
auto e9 = error_code(false, false, false, false);
std::cout << "(true, true, true, true)==0 -> " << (e5 == 0) << "\n";
std::cout << "(true, true, true, false)==4 -> " << (e6 == 4) << "\n";
std::cout << "(true, true, false, false)==3 -> " << (e7 == 3) << "\n";
std::cout << "(true, false, false, false)==2 -> " << (e8 == 2)<< "\n";
std::cout << "(false, false, false, false)==1 -> " << (e9 == 1)<< "\n";
}
我想知道error_code()
使用 c++14 / c++17 的新展开功能可以在哪里改进这个“”函数,因此它在表现力上有所提高并且使用的函数少于 3 个。
任何帮助将不胜感激!
解决方案
C++17 折叠:
template<class... Bools>
constexpr unsigned error_code(Bools... Bs) {
unsigned rv = 1;
(void) ((rv += Bs, !Bs) || ...);
return rv % (sizeof...(Bs) + 1);
}
不请自来,所以这只是一个奖励-同样的想法,C ++ 20:
constexpr unsigned error_code(auto... Bs) {
unsigned rv = 1;
(void) ((rv += Bs, !Bs) || ...);
return rv % (sizeof...(Bs) + 1);
}
解释:
fold 表达式的第一部分包含由 a 分隔的两部分
,
。左边部分的结果被丢弃,这样一个表达式的结果是最右边的部分,!Bs
。(rv += Bs, !Bs)
第二部分
|| ...
是折叠(或展开)的来源。第一个表达式被重复复制/粘贴,直到包中没有更多参数。因为true, false, true
它变成:(rv += 1, !true) || (rv += 0, !false) || (rv += 1, !true)
或者
(rv += 1, false) || (rv += 0, true) || (rv += 1, false)
短路评估开始。当内置的1运算符在左侧
||
有 a时,不会评估右侧。true
这就是为什么rv += 1
在这个例子中只完成了一个 's。停止评估,(rv += 0, true)
因此仅评估以下内容:(rv += 1, false) || (rv += 0, true)
最后
rv % (sizeof...(Bs) + 1);
是处理没有false
找到值并且我们应该返回的情况0
。例子:unsigned rv = 1; (rv += 1, !true) || (rv += 1, !true) || (rv += 1, !true); // rv is now 4, and sizeof...(Bs) == 3, so: 4 % (3 + 1) == 0
为什么
(void)
?
编译器喜欢警告他们认为未使用的表达式。一个仔细放置(void)
告诉它我们不在乎,所以它让编译器保持沉默。
1 - 这不适用于用户定义的运算符。
推荐阅读
- r - R 版本的 Vlookup,每个单元格有多个匹配项
- python - 如果前一组中存在,则删除行
- java - 当我们想要使用 map.get(key).wait() 时,当值是 AtomicLong 时同步 map.get(key) 真的有意义吗?
- javascript - eslint 动态改变文件列表
- sqlite - 使用存储在变量中的索引位置选择 SQLite3 数据
- javascript - 需要帮助以递归方式遍历 Javascript 对象的所有级别
- ajax - 为什么 if 条件不过滤结果?
- npm - 内部/模块/cjs/loader.js:584 问题
- clang - CXXRecordDecl 与子 VarDecl?
- typescript - 有没有办法在条件打字稿中使用扩展字符串进行推理?