为什么我们在迭代可变模板参数时必须使用额外的构造
Why do we have to use additional construction when iterating variadic template parameters?
为什么在迭代可变模板参数时必须使用额外的构造,如(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;
}
但为什么我们需要使用:
non(val += args ...);
而不是val += args...;
auto tmp = { val += args... };
而不是val += args...;
[](...){}((val += args)... );
而不是val += args...;
它会更清晰、更容易使用,所以:
template<typename T, typename ...Args>
inline T sum(T val, Args ...args) { val += args...; return val; }
为什么标准中没有这种可能性,或者这种可能性会带来任何危险?
在C++17或更高版本中会有这样的可能性吗?
这是因为参数包必须在需要语法列表的上下文中展开。正常的函数作用域不是这样的上下文,所以不能只编写val += args...;
。
然而,在C++17中,我们将获得fold表达式,这将允许您像这样重写代码:
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)
Fold表达式还支持扩展涉及参数包的任意表达式,如下所示:
(foo(args), ...);
((mymap[args] = 42), ...);
这使您能够轻松地在函数范围内使用参数包扩展表达式。
在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
相关文章:
- 使用std::multimap迭代器创建std::list
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- C++中带有List类的迭代器Segfault
- 迭代时从向量和内存中删除对象
- 为什么我们不在 STL 的迭代器中传递星号(*)
- 为什么我们在C++标准中没有"const 迭代器",而是const_iterator?
- 如何解决我们必须向前和向后迭代的项链断裂问题
- 检查迭代器是否具有我们要在C++中分配的值是否明智的优化
- C :在我们拥有迭代器的std :: unordered_set中替换一个元素
- 为什么我们要把 :: (范围重新定位运算符)放在迭代器之前
- 我们可以安全地依靠迭代器的v.end()位置
- 是我们迭代时更改哈希映射的行为,已定义
- 我们可以使用迭代器替换向量元素的值吗?
- 在类映射中,当我们有迭代器时,constiterator的必要性是什么
- 为什么我们在这里增加 ostream 迭代器
- 哪一个更快,为什么?1.阵列2.链接列表.如果我们只想在for循环中迭代并打印它
- 在多映射中,当两个迭代器持有具有映射到不同Value的相同键的值时.我们如何才能在地图上找到它们中的哪一个在另一个之前
- 为什么我们在迭代可变模板参数时必须使用额外的构造
- 使用迭代器的二叉搜索,为什么我们使用"(end - begin)/2"?
- C++为什么我们不能使用 > 和 < 来比较迭代器?