【问题标题】:Why do we have to use additional construction when iterating variadic template parameters?为什么我们在迭代可变参数模板参数时必须使用额外的构造?
【发布时间】:2016-08-02 10:59:08
【问题描述】:

为什么我们在迭代可变参数模板参数时必须使用额外的构造,例如(non() - 函数、temp[] - 数组或空[](...){} - lambda)?

众所周知,我们可以使用以下一些方法在 C++ 中使用可变参数模板迭代参数包:

http://ideone.com/GXDPDw

#include <iostream>
#include <cstdlib>
#include <valarray>
#include <numeric>
using namespace std;

template<typename ...Args> constexpr inline void non(Args ...) {}

template<typename T, typename ...Args>
inline T sum1(T val, Args ...args) { non(val += args ...); return val; }   // v1
// why do we need some function non() here?

template<typename T, typename ...Args>
inline T sum2(T val, Args ...args) { auto tmp = { val += args... }; return val; }   // v2
// why do we need some array tmp[] here?

template<typename T, typename ...Args>
inline T sum3(T val, Args ...args) { [](...){}((val += args)... ); return val; }   // v3
// why do we need empty lambda [](...){} here?

template<typename T, typename ...Args>
inline T sum4(T val, Args ...args) { for(auto &i:{ args... }) val += i; return val; }//v4

template<typename ...Args, typename T = common_type_t<Args...>>
inline T sum5(Args ...args) { return std::valarray<T>({ args... }).sum(); }   // v5


template<typename T> constexpr inline T sum6(T val) { return val; }

template<typename T, typename ...Args>
constexpr inline T sum6(T val, Args ...args) { return val + sum6(args...); }  // v6


int main() {    
    cout << sum1(1, 2, 3) << endl;
    cout << sum2(1, 2, 3) << endl;
    cout << sum3(1, 2, 3) << endl;
    cout << sum4(1, 2, 3) << endl;
    cout << sum5(1, 2, 3) << endl;
    cout << sum6(1, 2, 3) << endl;

    return 0;
}

但是为什么我们需要使用:

  1. non(val += args ...); 而不是 val += args...;
  2. auto tmp = { val += args... }; 而不是 val += args...;
  3. [](...){}((val += args)... ); 而不是 val += args...;

这样使用起来会更清晰、更容易:

template<typename T, typename ...Args>
inline T sum(T val, Args ...args) { val += args...; return val; }

为什么标准中没有这种可能性,或者这种可能性会带来什么危险?

在 C++17 或更高版本中会有这种可能性吗?

【问题讨论】:

  • 另外,使用if constexpr,您不需要额外的重载来处理可变参数模板参数包。
  • @Johannes Schaub - litb 谢谢。你的意思是这样的吗? melpon.org/wandbox/permlink/bNWHskOvPU7pieH5 但是在带有 c++1z 的 GCC 7.0 中仍然不支持这一点。它可能出现在哪个 c++17/... 标准中?

标签: c++ c++11 c++14 variadic-templates c++17


【解决方案1】:

这是因为参数包必须在需要语法列表的上下文中展开。普通的函数作用域不是这样的上下文,所以不能只写val += args...;

但是,在 C++17 中,我们将获得 fold expressions,这将允许您像这样重写您的代码:

template<typename T, typename ...Args>
inline T sum(T val, Args ...args) { (val += ... += args) ; return val; }

对于三个参数,这将扩展为 (((val += arg0)) += arg1) += arg2)

另一种选择是这样写:

val += (... + args);

这扩展为val += ((arg0 + arg1) + arg2)


折叠表达式还将支持扩展涉及参数包的任意表达式,如下所示:

(foo(args), ...);
((mymap[args] = 42), ...);

这使您能够轻松地在函数范围内使用参数包扩展表达式。

【讨论】:

  • 谢谢!是的,我可以使用标志 -std=c++1z 从 GCC 6.1 开始执行此操作:godbolt.org/g/CAo0Mp
  • 你真的需要提到基于,的折叠表达式。
  • @Yakk 这样更好吗?
【解决方案2】:

TarlanLlama 已经给出了答案。

在 C++17 中,http://en.cppreference.com/w/cpp/language/fold 可以重写为:

template<typename ...Args>
decltype(auto) sum(Args ...args) {
  return (... + args);
}

现场演示http://coliru.stacked-crooked.com/a/801be0c57eca8890

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-20
    • 2012-09-27
    • 2011-03-17
    • 2018-02-03
    • 2017-08-29
    • 1970-01-01
    • 2014-08-06
    相关资源
    最近更新 更多