请不要写这个:
for (const auto& s: decltype(argv)(argv.begin(), argv.end()-1)) {
首先,回溯时没有人(包括您)会理解这个内容。其次,由于
decltype(argv)
是一个
vector
, 这将复制大量元素...只是为了避免迭代其中一个元素?这非常浪费。
它还有另一个问题,你的第二个选项也存在这个问题。
这个:
for (size_t i = 0; i < argv.size() - 1; ++i) {
这段代码存在微妙的问题,因为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; }
};
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())};
}
这使您可以进行以下操作:
for (const auto& s : drop_last(argv)) { ... }
这很高效(避免了额外的复制),避免了未定义的行为(
drop_last()
始终提供有效范围),从名称上也很清楚它的作用。
argv.end()-1
和/或argv.size()-1
是否有效,否则不要这样做。 - Richard Critteni + 1 < argv.size()
- bipllsize()
是无符号的,而0ULL - 1
非常大? - chew socksargv
的最后一个元素是nulltpr
,因为它在execvp(argv[0], (char* const*)&argv[0])
中发生了。 - chew socks