自然数 n 的正式定义(在集合论中)如下:
我认为如果允许我这样做的话,这将使一些 C++ 代码变得更加简单:
for (int n : 10)
cout << n << endl;
它打印了从0到9的数字。
所以我尝试执行以下操作,但无法编译:
#include <iostream>
#include <boost/iterator/counting_iterator.hpp>
boost::counting_iterator<int> begin(int t)
{
return boost::counting_iterator<int>(0);
}
boost::counting_iterator<int> end(int t)
{
return boost::counting_iterator<int>(t);
}
int main()
{
for (int t : 10)
std::cout << t << std::endl;
return 0;
}
关于如何实现这一目标有什么建议吗?我使用 clang++ 时收到以下错误:
main.cpp:22:20: error: invalid range expression of type 'int'; no viable 'begin' function available
for (int t : 10)
^ ~~
但我认为我应该被允许这样做! :)
编辑:我知道如果我在 for 循环中添加单词“范围”(或其他单词),我可以“伪造”它,但我想知道是否可以不这样做。
这是做不到的。 来自C++ 14 标准草案第 6.5.4 节(但 C++11 将非常相似)
begin-expr 和 end-expr 确定如下:
(1.1) — 如果 _
是数组类型,[...];RangeT
嗯,这显然不适用。
int
不是数组
(1.2) — 如果 _RangeT 是类类型,[...]
不,这也不适用。
(1.3) — 否则,begin-expr 和 end-expr 分别是
和begin(__range)
,end(__range)
哦! 这看起来很有希望。 您可能需要将
begin
和 end
移至全局命名空间,但仍然...
哪里
和begin
在关联的命名空间中查找 (3.4.2)。 [ 注意:普通的不合格查找(3.4.1) 不执行。 — 尾注]end
(强调我的)。 打扰! 没有任何与
int
关联的命名空间。 具体来说,来自第 3.4.2 节
— 如果 T [在我们的例子中为
] 是基本类型,则其关联的命名空间和类集都是空的。int
唯一的解决方法是编写一个具有合适的开始和结束方法的类
range
。 然后你就可以写出非常Pythonic的代码了:
for (int i : range(5))
如果您查看 基于范围的
for
循环的 cppreference 页面,或者更好的是标准的相关部分 ([stmt.ranged]p1),您会看到它如何确定 begin_expr
用于循环。与 int
相关的是
((1.3) 否则,begin-expr 和 end-expr 分别是
begin(__range)
和end(__range)
,其中 begin 和 end 在关联的命名空间中查找 ([basic.lookup.argdep] )。 [ 注意:不执行普通的不合格查找([basic.lookup.unqual])。 — 尾注 ]
强调已添加)
不幸的是,对于用例来说,对于诸如int
之类的基本类型,依赖于参数的查找永远不会返回任何内容。相反,您可以声明一个类作为范围表达式,并为其提供
begin
和
end
方法:
struct Range {
using value_type = unsigned int;
using iterator = boost::counting_iterator<value_type>;
unsigned int max;
iterator begin() const
{
return iterator(0);
}
iterator end() const
{
return iterator(max);
}
};
此类的潜在改进包括:
begin
和
end
方法
constexpr
(这需要编写您自己的
boost::counting_iterator
版本,或者让 Boost 制作他们的版本
constexpr
)
Range operator""_range
unsigned int
以外的类型。
for (int n : Range{10}) {
std::cout << n << '\n';
}
您仍然无法直接使用 int
,但您可以接近。
您已将此问题标记为 C++14,因此您可以使用
std::integer_sequence
和
std::make_integer_sequence
。如果自然数在编译时已知(如示例中的
10
),您可以编写一个简单的
constexpr
函数
getArray()
(带有辅助函数
getArrayH
)来获取从零到值的
std::array
N-1
template <typename T, T ... Vals>
constexpr std::array<T, sizeof...(Vals)>
getArrayH (std::integer_sequence<T, Vals...> const &)
{ return { { Vals... } }; }
template <typename T, T Val>
constexpr auto getArray ()
{ return getArrayH(std::make_integer_sequence<T, Val>{}); }
并称之为
for ( auto const & i : getArray<int, 10>() )
以下是完整的工作示例
#include <array>
#include <utility>
#include <iostream>
template <typename T, T ... Vals>
constexpr std::array<T, sizeof...(Vals)>
getArrayH (std::integer_sequence<T, Vals...> const &)
{ return { { Vals... } }; }
template <typename T, T Val>
constexpr auto getArray ()
{ return getArrayH(std::make_integer_sequence<T, Val>{}); }
int main ()
{
for ( auto const & i : getArray<int, 10>() )
std::cout << i << std::endl;
}