首页 > 解决方案 > 使用新的 c++14 / c++17 功能改进可变参数模板函数

问题描述

我是可变参数模板的新手,我仍然设法使用它在 c++11 中编写了一些代码,但我仍然对结果感到不满,因为它缺乏表现力。

问题是实现一个函数,该函数接受多个bool条件(从到任何条件)并返回一个整数代码,指示第一个“ ”参数1在什么位置,或者它们是否都是.false0true

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++c++14c++17variadic-templatesfold-expression

解决方案


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 - 这不适用于用户定义的运算符。


推荐阅读