che*_*cks 9 c++ iterator vector std c++11
当从C++ 11的开头迭代std::vector到第二个到最后一个元素时,首选的样式是什么?
std::vector<const char*> argv;
std::string str;
Run Code Online (Sandbox Code Playgroud)
应该使用这种更多的C++ - esque方法
for (const auto& s: decltype(argv)(argv.begin(), argv.end()-1)) {
str += std::string(s) + ' ';
}
Run Code Online (Sandbox Code Playgroud)
还是应该采用更传统的方式?
for (size_t i = 0; i < argv.size() - 1; ++i) {
str += std::string(argv[i]);
}
Run Code Online (Sandbox Code Playgroud)
Bar*_*rry 12
请不要这样写:
for (const auto& s: decltype(argv)(argv.begin(), argv.end()-1)) {
Run Code Online (Sandbox Code Playgroud)
首先,当你回顾它时,没有人(包括你)会理解这一点.其次,既然decltype(argv)是a vector,这就是复制了一大堆元素......只是因为你想避免迭代其中一个元素?那太浪费了.
它还有另一个问题,您的第二个选项会共享该问题.
这个:
for (size_t i = 0; i < argv.size() - 1; ++i) {
Run Code Online (Sandbox Code Playgroud)
因为size()是无符号的,所以更微妙的问题.因此,如果argv恰好是空的,argv.size() - 1将会是一些非常大的数字,并且您实际上将访问数组的所有这些无效元素,从而导致未定义的行为.
对于迭代器,如果argv.begin() == argv.end(),则无法从获得以前的迭代end(),因为是从没有先前的迭代器end().所有的end() - 1,prev(end())和--end()未定义行为已.那时,我们甚至无法推断循环会做什么,因为我们甚至没有有效的范围.
我建议的是:
template <typename It>
struct iterator_pair {
It b, e;
It begin() const { return b; }
It end() const { return e; }
};
// this doesn't work for types like raw arrays, I leave that as
// an exercise to the reader
template <typename Range>
auto drop_last(Range& r)
-> iterator_pair<decltype(r.begin())>
{
return {r.begin(), r.begin() == r.end() ? r.end() : std::prev(r.end())};
}
Run Code Online (Sandbox Code Playgroud)
这可以让你做到:
for (const auto& s : drop_last(argv)) { ... }
Run Code Online (Sandbox Code Playgroud)
这是有效的(避免额外的副本),避免未定义的行为(drop_last()总是给出一个有效的范围),并且很清楚它从名称中做了什么.
我发现第一个选项有点笨拙,但是因为这是个人偏好的问题,我提出了一种避免手写循环的备选方法(并且在假设的情况下argv.size() >= 1),这在某种意义上可能更好减少错别字和索引错误的可能性.
#include <numeric>
std::string str;
if (!argv.empty())
str = std::accumulate(argv.begin(), std::prev(argv.end()), str);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
499 次 |
| 最近记录: |