让我们说,我有一个提供c
方法的类型的容器size()
,我想循环遍历此容器,同时跟踪每个项目的索引:
for (/*TODO*/ i = 0; i < c.size(); i++) {...}
在后C ++ 11世界中,自动类型演绎很好地解决了很多问题。我们应该用什么代替上面的TODO
?无论size()
的类型是什么,对我来说唯一正确的是以下内容:
for (decltype(c.size()) i = 0; i < c.size(); i++) {...}
但这看起来过于冗长,而且在我看来,对可读性没有帮助。
另一个解决方案可能是:
for (auto end = c.size(), i = 0; i < end; i++) {...}
但这对可读性也没有帮助,当然,它与原始片段没有相同的语义。
所以,我的问题是:在只给出索引的类型的情况下,推断循环索引变量类型的最佳方法是什么?限制。
答案 0 :(得分:3)
对文字中第一个问题的简短回答:您应该将/*TODO*/
替换为unsigned
,std::size_t
或类似内容,这意味着:不要去除推断类型,只需选择适合任何合理容器尺寸的类型。
这将是一个无符号,相当大的类型,因此编译器不会因为可能的精度损失而对你大喊大叫。在上面的评论中,您写道size_t
不是保证是decltype(c.size())
的良好替代品,但是实现具有索引不兼容的容器并非不可能size_t
,这样的indizes肯定不是数字(因此与i = 0
不兼容),容器也不会有size
方法。 size()
方法意味着非负整数,并且由于size_t
是针对这些数字设计的,因此几乎不可能有一个无法用它表示的大小的容器。
您的第二个问题旨在如何推断出类型,并且您已经提供了最简单但不完美的答案。如果你想要一个不像decltype
那样冗长的解决方案,而不是像auto end
那样令人惊讶,你可以在某个实用程序头中为起始索引定义模板别名和生成器函数:
template <class T>
using index_t = decltype(std::declval<T>().size());
template <class T, class U>
constexpr index_t<T> index(T&&, U u) { return u; }
//and then in the actual location of the loop:
for (auto i = index(c,0); i < c.size(); ++i) {...}
//which is the same as
for (auto i = index_t<std::vector<int>>(0); i < c.size(); ++i) {...}
如果您想要更通用的索引类型,例如对于没有size
方法的数组和类,它会变得有点复杂,因为模板别名可能不是专门的:
template <class T>
struct index_type {
using type = decltype(std::declval<T>().size());
};
template <class T>
using index_t = typename index_type<T>::type;
template <class T, class U>
constexpr index_t<T> index(T&&, U u) { return u; }
//index_type specializations
template <class U, std::size_t N>
struct index_type<U[N]> {
using type = decltype(N);
};
template <>
struct index_type<System::AnsiString::AnsiString> { //YUCK! VCL!
using type = int;
};
然而,对于少数几个你真正需要索引并且简单的foreach循环是不够的情况,这是很多东西。
答案 1 :(得分:1)
如果c是容器,您可以使用container::size_type
。
答案 2 :(得分:0)
事实上,我已经看过很多次(咳嗽 llvm,clang)他们在哪里使用
for (/* type */ iter = begin(), End = end(); iter != End; ++i);
在开始时评估End
的优点是编译器可以确保它不需要每次都调用它。对于那些计算结束是微不足道的集合,编译器已经能够推断它不需要多次调用end(),它不会有帮助,但在其他情况下它会。
或者你总是可以使用帮手:
答案 3 :(得分:0)
嗯......这需要C ++ 14或支持lambda参数中的auto的编译器。如果您经常使用这种模式,那么帮助功能可能会很有用:
template< typename Container, typename Callable >
void for_each_index( Container& container, Callable callable )
{
for (decltype(container.size()) i = 0; i < container.size(); i++)
{
callable(i);
}
}
用作:
for_each_index(c, [] (auto index) {
// ...
});
答案 4 :(得分:0)
以下是我遵循的优先顺序
1)
range-for
2)iterator/begin()/end()
,其类型由auto
推断。
对于需要索引的情况,这是主题,我更喜欢使用
for( auto i = 0u; i < c.size(); ++i) {...}
即使我错过了在u
中添加0
,编译器也会警告我。
如果它不是太冗长会爱decltype
for (decltype(c.size()) i = 0; i < c.size(); i++) {...}