【发布时间】:2015-03-30 07:03:00
【问题描述】:
对于 for-range 循环语法,如何使原始指针表现得像一个范围。
double five = 5;
double* dptr = &five;
for(int& d : dptr) std::cout << d << std::endl;// will not execute if the pointer is null
动机:
现在,boost::optional(未来的std::optional)值可以被视为一个范围,因此可以在 for range 循环http://faithandbrave.hateblo.jp/entry/2015/01/29/173613 中使用。
当我重写我自己的简化版本时:
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;
}
}
用作
boost::optional<int> opt = 3;
for (int& x : opt) std::cout << x << std::endl;
在查看该代码时,我想它也可以泛化为原始(可为空)指针。
double five = 5;
double* dptr = &five;
for(int& d : dptr) std::cout << d << std::endl;
而不是通常的if(dptr) std::cout << *dptr << std::endl;。这很好,但我想实现上面的其他语法。
尝试
首先,我尝试使上述Optional 版本的begin 和end 用于指针,但我做不到。所以我决定明确类型并删除所有模板:
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;
}
}
差不多了,它适用于
for(double* ptr = std::begin(dptr); ptr != std::end(dptr); ++ptr)
std::cout << *ptr << std::endl;
但它不适用于 假定等效的 for-range 循环:
for(double& d : dptr) std::cout << d << std::endl;
两个编译器告诉我:error: invalid range expression of type 'double *'; no viable 'begin' function available
发生了什么事?是否有一种编译器魔法禁止范围循环为指针工作。我是否对范围循环语法做出了错误的假设?
具有讽刺意味的是,在标准中,std::begin(T(&arr)[N]) 有一个重载,这非常接近它。
请注意一下
是的,这个想法很愚蠢,因为即使可能,这也会非常令人困惑:
double* ptr = new double[10];
for(double& d : ptr){...}
只会遍历第一个元素。一个更清晰、更现实的解决方法是做类似@Yakk 提出的解决方法:
for(double& d : boost::make_optional_ref(ptr)){...}
通过这种方式,很明显我们只迭代了一个元素,并且该元素是可选的。
好的,好的,我回if(ptr) ... use *ptr。
【问题讨论】:
-
ADL 永远无法找到您的
begin和end,因此基于范围的for将不起作用。而且我不明白如何将optional视为范围?!您应该能够通过使用这些指针构造一个boost::iterator_range来让您的示例工作,但是以您的方式形成一个标量对象的end迭代器 很可能是未定义的行为。跨度> -
@Praetorian 实际上是合法的;出于指针算术目的,不是数组元素的东西被认为是大小为 1 的数组。不过,将重载添加到
namespace std绝对是 UB,并且为此滥用基于范围的for是相当愚蠢的。跨度> -
@T.C.啊,好的,谢谢你的澄清,我不确定那部分。
-
奇怪的是
std::begin(T(&arr)[N])不用于基于范围的 for 循环。核心语言直接处理数组:“如果_RangeT是数组类型,begin-expr和end-expr分别是__range和__range + __bound,其中__bound是数组绑定。"
标签: c++ pointers c++11 for-loop boost-optional