在基于范围的for-loop中查看原始指针作为范围 [英] Viewing a raw pointer as a range in range-based for-loop

查看:291
本文介绍了在基于范围的for-loop中查看原始指针作为范围的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  double five = 

5;
double * dptr =& five;
for(int& d:dptr)std :: cout<< d<< std :: endl; //如果指针为空则不会执行

动机: / strong>



现在是vox populi, boost :: optional (future std :: optional )值可以被视为一个范围,因此用于范围循环 http://faithandbrave.hateblo.jp/entry/2015/01/29/173613



当我重写我自己的简化版本时:

 命名空间boost {
template< class Optional>
decltype(auto)begin(可选& opt)noexcept {
return opt?& * opt:nullptr;
}

template< class Optional>
decltype(auto)end(可选& opt)noexcept {
return opt?std :: next(& * opt):nullptr;
}
}

用作

  boost :: optional< int> opt = 3; 
for(int& x:opt)std :: cout<< x < std :: endl;

在我看来,我想象它可以推广到原始(可空)指针。 / p>

  double five = 5; 
double * dptr =& five;
for(int& d:dptr)std :: cout<< d<< std :: endl;

而不是通常的 if(dptr)std :: cout<< ; * dptr<< std :: endl;


$ b



>首先我尝试使可选版本开始结束工作指针,但我不能。所以我决定在类型和显式删除所有模板:

  namespace std {//打扰我, ,命名空间可以删除,但效果是一样的。 
double * begin(double * opt){
return opt?&*; opt:nullptr;
}
double * end(double * opt){
return opt?std :: next(& * opt):nullptr;
}
}

几乎在那里,它适用于

  for(double * ptr = std :: begin(dptr); ptr!= std :: end(dptr); ++ ptr)
std :: cout<< * ptr<< std :: endl;

但它不适用于假定的等效 :

  for(double& d:dptr)std :: cout< d<< std :: endl; 

两个编译器告诉我:错误: ';没有可行的开始功能可用



发生了什么?有一个编译器的魔法,禁止远程循环工作的指针。



讽刺的是,在标准中有一个 std的重载: :begin(T(& arr)[N]),这非常接近它。





$ b b

注意第二个



是的,这个想法很愚蠢,因为即使可能, p>

  double * ptr = new double [10]; 
for(double& d:ptr){...}

第一元素。一个更清楚也是现实的解决方法是做一些像@Yakk提出的解决方案:

  for(double& d:boost :: make_optional_ref(ptr)){...} 



好的,我会回到 if(ptr)... use因为基于范围的作品的方式是(从§6.5。)。 4):




begin-expr end-expr

- 如果 _RangeT 是数组类型,[..]

- 如果 _RangeT 是类类型,[..]

- 否则, begin-expr end-expr 都是 begin(__ range) end(__ range),其中 begin
end 在相关命名空间(3.4.2)中查找。 [注意:不执行普通无限额查找(3.4.1)
-end note]


这种情况下相关的命名空间是什么? (§3.4.2/ 2,强调我):


命名空间和类的集合通过以下方式确定:

(2.1) - 如果 T 是基本类型,其关联的命名空间和类的集合都为空


因此,没有地方可以让你的 double * begin(double *)它将由基于范围的 for 语句调用。



您想做的只是制作一个简单的包装器的解决方法:

  template< typename T> 
struct PtrWrapper {
T * p;
T * begin()const {return p; }
T * end()const {return p? p + 1:nullptr; }
};

for(double& d:PtrWrapper< double> {dptr}){..}


How can I make a raw pointer behave like a range, for a for-range loop syntax.

double five = 5;
double* dptr = &five;
for(int& d : dptr) std::cout << d << std::endl;// will not execute if the pointer is null

Motivation:

It is now vox populi that an boost::optional (future std::optional) value can be viewed as a range and therefore used in a for range loop http://faithandbrave.hateblo.jp/entry/2015/01/29/173613.

When I rewrote my own simplified version of it:

namespace boost {
    template <class Optional>
    decltype(auto) begin(Optional& opt) noexcept{
        return opt?&*opt:nullptr;
    }

    template <class Optional>
    decltype(auto) end(Optional& opt) noexcept{
        return opt?std::next(&*opt):nullptr;
    }
}

Used as

boost::optional<int> opt = 3;
for (int& x : opt) std::cout << x << std::endl;

While looking that code I imagined that it could be generalized to raw (nullable) pointers as well.

double five = 5;
double* dptr = &five;
for(int& d : dptr) std::cout << d << std::endl;

instead of the usual if(dptr) std::cout << *dptr << std::endl;. Which is fine but I wanted to achieve the other syntax above.

Attempts

First I tried to make the above Optional version of begin and end work for pointers but I couldn't. So I decided to be explicit in the types and remove all templates:

namespace std{ // excuse me, this for experimenting only, the namespace can be removed but the effect is the same.
    double* begin(double* opt){
        return opt?&*opt:nullptr;
    }
    double* end(double* opt){
        return opt?std::next(&*opt):nullptr;
    }
}

Almost there, it works for

for(double* ptr = std::begin(dptr); ptr != std::end(dptr); ++ptr) 
    std::cout << *ptr << std::endl;

But it doesn't work for the supposedly equivalent for-range loop:

for(double& d : dptr) std::cout << d << std::endl;

Two compilers tell me: error: invalid range expression of type 'double *'; no viable 'begin' function available

What is going on? Is there a compiler magic that forbids the ranged-loop to to work for pointers. Am I making a wrong assumption about the ranged-loop syntax?

Ironically, in the standard there is an overload for std::begin(T(&arr)[N]) and this is very close to it.


Note and a second though

Yes, the idea is silly because, even if possible this would be very confusing:

double* ptr = new double[10];
for(double& d : ptr){...}

would iterate over the first element only. A more clear and also realistic workaround would be to do something like workaround proposed by @Yakk:

for(double& d : boost::make_optional_ref(ptr)){...}

In this way it is clear that we are iterating over one element only and that that element is optional.

Ok, ok, I will go back to if(ptr) ... use *ptr.

解决方案

Because the way that range-based for works is (from §6.5.4):

begin-expr and end-expr are determined as follows
— if _RangeT is an array type, [..]
— if _RangeT is a class type, [..]
— otherwise, begin-expr and end-expr are begin(__range) and end(__range), respectively, 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 ]

What are the associated namespaces in this case? (§3.4.2/2, emphasis mine):

The sets of namespaces and classes are determined in the following way:
(2.1) — If T is a fundamental type, its associated sets of namespaces and classes are both empty.

Thus, there is no place to put your double* begin(double*) such that it will be called by the range-based for statement.

A workaround for what you want to do is just make a simple wrapper:

template <typename T> 
struct PtrWrapper {
    T* p;
    T* begin() const { return p; }
    T* end() const { return p ? p+1 : nullptr; }
};

for (double& d : PtrWrapper<double>{dptr}) { .. }

这篇关于在基于范围的for-loop中查看原始指针作为范围的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆