像许多人一样,这些天我一直在尝试c++ 11带来的不同特性。我最喜欢的是“基于范围的for循环”。
我明白:
for(Type& v : a) { ... }
等价于:
for(auto iv = begin(a); iv != end(a); ++iv)
{
Type& v = *iv;
...
}
begin()只是返回标准容器的a.begin()。
但是如果我想让我的自定义类型“基于范围的for循环”感知呢?
我应该专门化begin()和end()吗?
如果我的自定义类型属于命名空间xml,我应该定义xml::begin()或std::begin() ?
简而言之,这样做的指导方针是什么?
我写下我的答案是因为有些人可能更喜欢简单的现实生活的例子,没有STL包含。
出于某种原因,我有自己的纯数据数组实现,我想使用基于范围的for循环。以下是我的解决方案:
template <typename DataType>
class PodArray {
public:
class iterator {
public:
iterator(DataType * ptr): ptr(ptr){}
iterator operator++() { ++ptr; return *this; }
bool operator!=(const iterator & other) const { return ptr != other.ptr; }
const DataType& operator*() const { return *ptr; }
private:
DataType* ptr;
};
private:
unsigned len;
DataType *val;
public:
iterator begin() const { return iterator(val); }
iterator end() const { return iterator(val + len); }
// rest of the container definition not related to the question ...
};
然后是用法示例:
PodArray<char> array;
// fill up array in some way
for(auto& c : array)
printf("char: %c\n", c);
我写下我的答案是因为有些人可能更喜欢简单的现实生活的例子,没有STL包含。
出于某种原因,我有自己的纯数据数组实现,我想使用基于范围的for循环。以下是我的解决方案:
template <typename DataType>
class PodArray {
public:
class iterator {
public:
iterator(DataType * ptr): ptr(ptr){}
iterator operator++() { ++ptr; return *this; }
bool operator!=(const iterator & other) const { return ptr != other.ptr; }
const DataType& operator*() const { return *ptr; }
private:
DataType* ptr;
};
private:
unsigned len;
DataType *val;
public:
iterator begin() const { return iterator(val); }
iterator end() const { return iterator(val + len); }
// rest of the container definition not related to the question ...
};
然后是用法示例:
PodArray<char> array;
// fill up array in some way
for(auto& c : array)
printf("char: %c\n", c);
我想详细解释一下@Steve Jessop回答的一些部分,一开始我不明白。希望能有所帮助。
std::begin calls the begin() member function anyway, so if you
only implement one of the above, then the results should be the same
no matter which one you choose. That's the same results for
ranged-based for loops, and also the same result for mere mortal code
that doesn't have its own magical name resolution rules so just does
using std::begin; followed by an unqualified call to begin(a).
If you implement the member functions and the ADL functions, though,
then range-based for loops should call the member functions, whereas
mere mortals will call the ADL functions. Best make sure they do the
same thing in that case!
https://en.cppreference.com/w/cpp/language/range-for:
如果……
如果range_expression是类类型C的表达式,该表达式同时具有一个名为begin的成员和一个名为end的成员
),则begin_expr为
__range.begin()和end_expr是__range.end();
否则,begin_expr是begin(__range), end_expr是end(__range),它们是通过依赖参数的查找(非adl)找到的
不执行查找)。
对于基于范围的For循环,首先选择成员函数。
但对于
using std::begin;
begin(instance);
首先选择ADL函数。
例子:
#include <iostream>
#include <string>
using std::cout;
using std::endl;
namespace Foo{
struct A{
//member function version
int* begin(){
cout << "111";
int* p = new int(3); //leak I know, for simplicity
return p;
}
int *end(){
cout << "111";
int* p = new int(4);
return p;
}
};
//ADL version
int* begin(A a){
cout << "222";
int* p = new int(5);
return p;
}
int* end(A a){
cout << "222";
int* p = new int(6);
return p;
}
}
int main(int argc, char *args[]){
// Uncomment only one of two code sections below for each trial
// Foo::A a;
// using std::begin;
// begin(a); //ADL version are selected. If comment out ADL version, then member functions are called.
// Foo::A a;
// for(auto s: a){ //member functions are selected. If comment out member functions, then ADL are called.
// }
}
我应该专门化begin()和end()吗?
据我所知,这就足够了。您还必须确保对指针进行递增可以从开始到结束。
下一个示例(它缺少begin和end的const版本)编译并正常工作。
#include <iostream>
#include <algorithm>
int i=0;
struct A
{
A()
{
std::generate(&v[0], &v[10], [&i](){ return ++i;} );
}
int * begin()
{
return &v[0];
}
int * end()
{
return &v[10];
}
int v[10];
};
int main()
{
A a;
for( auto it : a )
{
std::cout << it << std::endl;
}
}
下面是另一个使用begin/end作为函数的例子。它们必须和类在同一个命名空间中,因为ADL:
#include <iostream>
#include <algorithm>
namespace foo{
int i=0;
struct A
{
A()
{
std::generate(&v[0], &v[10], [&i](){ return ++i;} );
}
int v[10];
};
int *begin( A &v )
{
return &v.v[0];
}
int *end( A &v )
{
return &v.v[10];
}
} // namespace foo
int main()
{
foo::A a;
for( auto it : a )
{
std::cout << it << std::endl;
}
}
在这里,我将分享一个创建自定义类型的最简单的例子,它将与“基于范围的for循环”一起工作:
#include<iostream>
using namespace std;
template<typename T, int sizeOfArray>
class MyCustomType
{
private:
T *data;
int indx;
public:
MyCustomType(){
data = new T[sizeOfArray];
indx = -1;
}
~MyCustomType(){
delete []data;
}
void addData(T newVal){
data[++indx] = newVal;
}
//write definition for begin() and end()
//these two method will be used for "ranged based loop idiom"
T* begin(){
return &data[0];
}
T* end(){
return &data[sizeOfArray];
}
};
int main()
{
MyCustomType<double, 2> numberList;
numberList.addData(20.25);
numberList.addData(50.12);
for(auto val: numberList){
cout<<val<<endl;
}
return 0;
}
希望,这将有助于一些新手开发像我:p:)
谢谢你!