C++ 中的 n={0,1,...,n-1}

n={0,1,...,n-1} in C++

自然数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)
                ^ ~~

但我认为我应该被允许这样做! :)

编辑:我知道我可以 "fake" 如果我在 for 循环中添加单词 "range" (或其他单词),但我我想知道是否可以不这样做。

您可以使用一些接近您想要的语法,但您需要一个包含要迭代的数字的数组。

int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
for (int n : a)
    std::cout << n << std::endl;

http://en.cppreference.com/w/cpp/language/range-for

编辑:创建数组而不声明每个值你可以检查这个问题:Is there a compact equivalent to Python range() in C++/STL

做不到。来自 draft of the C++ 14 standard 的第 6.5.4 节(但 C++11 将非常相似)

begin-expr and end-expr are determined as follows:

(1.1) — if _RangeT is an array type, [...];

嗯,这个显然不适用。 int 不是数组

(1.2) — if _RangeT is a class type, [...]

不,这也不适用。

(1.3) — otherwise, begin-expr and end-expr are begin(__range) and end(__range), respectively,

哎呀!这看起来很有希望。您可能需要将 beginend 移动到全局命名空间中,但仍然...

where begin and end are looked up in the associated namespaces (3.4.2). [ Note: Ordinary unqualified lookup (3.4.1) is not performed. — end note ]

(强调我的)。烦!没有任何名称空间与 int 关联。具体来说,从第 3.4.2 节

— If T [int in our case] is a fundamental type, its associated sets of namespaces and classes are both empty.

唯一的解决方法是编写一个 class range,它具有合适的开始和结束方法。然后你可以写出非常 pythonic:

for (int i : range(5))

纯属娱乐...

您已将此问题标记为 C++14,因此您可以使用 std::integer_sequencestd::make_integer_sequence

如果自然数是已知的编译时间(如您的示例中的 10),您可以编写一个简单的 constexpr 函数 getArray()(使用辅助函数 getArrayH ) 得到从零到 N-1

std::array 个值
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;
 }

如果您查看 the cppreference page for range-based for loops, or better yet the relevant section of the standard ([stmt.ranged]p1),您会看到它如何确定要用于循环的 begin_exprint 的相关项是

(1.3) otherwise, begin-expr and end-expr are begin(__range) and end(__range), respectively, where begin and end are looked up in the associated namespaces ([basic.lookup.argdep]). [ Note: Ordinary unqualified lookup ([basic.lookup.unqual]) is not performed. — end note ]

强调 添加)

不幸的是,对于用例,对于 int 等基本类型,依赖于参数的查找从来没有 returns 任何东西。

相反,你可以做的是声明一个 class 作为范围表达式,并给它 beginend 方法:

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);
    }
};

对此 class 的潜在改进包括:

  • 制作 beginend 方法 constexpr(这需要编写您自己的 boost::counting_iterator 版本,或者让 Boost 制作他们的版本 constexpr )
  • 添加用户定义的文字选项,如 Range operator""_range
  • 使其适用于 unsigned int 以外的类型。

live demo