Viewing a raw pointer as a range in range-based for-loop

风格不统一 提交于 2019-12-01 20:18:18

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}) { .. }

It is a useful lie to think that for(:) loops are implemented by "calling std::begin and std::end in a ADL-activated context". But that is a lie.

The standard instead basically does a parallel implementation of the std::begin and std::end in itself. This prevents the language's low level constructs from depending on its own library, which seems like a good idea.

The only lookup for begin by the language is the ADL-based lookup. Your pointer's std::begin won't be found, unless you are a pointer to something in std. The std::begin( T(&)[N} ) isn't found this way by the compiler, but instead that iteration is hard-coded by the language.

namespace boost {
  template<class T>
  T* begin( optional<T>&o ) {
    return o?std::addressof(*o):nullptr;
  }
  template<class T>
  T* begin( optional<T&>&&o ) {
    return o?std::addressof(*o):nullptr;
  }
  template<class T>
  T const* begin( optional<T> const&o ) {
    return o?std::addressof(*o):nullptr;
  }
  template<class T>
  T* end( optional<T>&o ) {
    return o?std::next(begin(o)):nullptr;
  }
  template<class T>
  T* end( optional<T&>&&o ) {
    return o?std::next(begin(o)):nullptr;
  }
  template<class T>
  T const* end( optional<T> const&o ) {
    return o?std::next(begin(o)):nullptr;
  }
  template<class T>
  boost::optional<T&> as_optional( T* t ) {
    if (t) return *t;
    return {};
  }
}

now you can:

void foo(double * d) {
  for(double& x : boost::as_optional(d)) {
    std::cout << x << "\n";
}

without having to repeat the type double.

Note that an rvalue optional to a non-reference returns a T const*, while an rvalue optonal to a T& returns a T*. Iterating over a temporary in a writing context is probably an error.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!