[range] const-only UDT works as std algos, but not with boost range.
A class that exposes a const_iterator, but does not expose a mutable iterator, works with std:: algorithms and range-for() loop, but does not work with boost range. // This works with std algorithms and range-for, but not with boost range. class C { public: typedef blah const_iterator; const_iterator begin() const; const_iterator end() const; }; const C c; for (auto x : c) { } // OK std::for_each(std::begin(c), std::end(c), fn); // OK boost::for_each (c, fn); // ERROR: fails SinglePassRange concept (GCC-6.2) The workaround is to add a non-const iterator type that is the same as the const iterator, and to add two additional non-const begin/end methods. class D { public: typedef blah const_iterator; const_iterator begin() const; const_iterator end() const; // kludge to make it work with boost. typedef const_iterator iterator; iterator begin(); iterator end(); }; const D d; boost::for_each (d, fn); // OK Is there a simpler solution?
On 7 September 2016 at 22:18, John
A class that exposes a const_iterator, but does not expose a mutable iterator, works with std:: algorithms and range-for() loop, but does not work with boost range.
This is true because it does not meet the requirements of the Boost.Range Concepts (see http://www.boost.org/doc/libs/1_61_0/libs/range/doc/html/range/concepts/sing...). To range requires a boost::range_iterator<T> and a boost::range_iterator<const T>. There are several solutions. The typical one is to simply add using iterator = const_iterator into the type to model the range concept. However if one requires a non-intrusive solution one can provide a specialization of boost::range_iterator. This design decision for the concepts means that there need not be const and mutable concepts for the ranges. It also makes the selection of overrides work with typical models of the Range Concepts such as Containers.
// This works with std algorithms and range-for, but not with boost range. class C { public: typedef blah const_iterator; const_iterator begin() const; const_iterator end() const; }; const C c; for (auto x : c) { } // OK std::for_each(std::begin(c), std::end(c), fn); // OK boost::for_each (c, fn); // ERROR: fails SinglePassRange concept (GCC-6.2)
The workaround is to add a non-const iterator type that is the same as the const iterator, and to add two additional non-const begin/end methods.
This is more than necessary to fix the problem. You only need to add using iterator = const_iterator; into class D. The additional begin() and end() functions that return iterator are not required.
class D { public: typedef blah const_iterator; const_iterator begin() const; const_iterator end() const;
// kludge to make it work with boost. typedef const_iterator iterator; iterator begin(); iterator end(); };
const D d; boost::for_each (d, fn); // OK
Is there a simpler solution?
Yes, as described above all you need is one line so it models a Range Concept. In other scenarios you might want to look at boost::sub_range which will preserve const-ness from the wrapped type while providing both const_iterator and iterator types (http://www.boost.org/doc/libs/1_36_0/libs/range/doc/utility_class.html).
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
I hope this helps. Regards, Neil Groves
Thanks, Why does boost range require a public non-const iterator that will never be used, yet the std:: framework does not? Is this intentional? On 09/08/2016 08:08 AM, Neil Groves wrote:
On 7 September 2016 at 22:18, John
wrote: A class that exposes a const_iterator, but does not expose a mutable iterator, works with std:: algorithms and range-for() loop, but does not work with boost range.
This is true because it does not meet the requirements of the Boost.Range Concepts (see http://www.boost.org/doc/libs/1_61_0/libs/range/doc/html/range/concepts/sing...). To range requires a boost::range_iterator<T> and a boost::range_iterator<const T>. There are several solutions. The typical one is to simply add using iterator = const_iterator into the type to model the range concept. However if one requires a non-intrusive solution one can provide a specialization of boost::range_iterator. This design decision for the concepts means that there need not be const and mutable concepts for the ranges. It also makes the selection of overrides work with typical models of the Range Concepts such as Containers.
// This works with std algorithms and range-for, but not with boost range. class C { public: typedef blah const_iterator; const_iterator begin() const; const_iterator end() const; }; const C c; for (auto x : c) { } // OK std::for_each(std::begin(c), std::end(c), fn); // OK boost::for_each (c, fn); // ERROR: fails SinglePassRange concept (GCC-6.2)
The workaround is to add a non-const iterator type that is the same as the const iterator, and to add two additional non-const begin/end methods.
This is more than necessary to fix the problem. You only need to add using iterator = const_iterator; into class D. The additional begin() and end() functions that return iterator are not required.
class D { public: typedef blah const_iterator; const_iterator begin() const; const_iterator end() const;
// kludge to make it work with boost. typedef const_iterator iterator; iterator begin(); iterator end(); };
const D d; boost::for_each (d, fn); // OK
Is there a simpler solution?
Yes, as described above all you need is one line so it models a Range Concept. In other scenarios you might want to look at boost::sub_range which will preserve const-ness from the wrapped type while providing both const_iterator and iterator types (http://www.boost.org/doc/libs/1_36_0/libs/range/doc/utility_class.html).
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
I hope this helps.
Regards, Neil Groves _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
On 8 September 2016 at 15:20, John
Thanks, Why does boost range require a public non-const iterator that will never be used, yet the std:: framework does not? Is this intentional?
It is intentional. The range for each for example came in C++11 and relies heavily on auto for type deduction (http://en.cppreference.com/w/cpp/language/range-for). Boost.Range Concepts were designed long before C++11 and therefore had to include to determine the type from a const and mutable range that worked with std:: containers. Additionally we wanted a design that could take other collections such as MFC/ATL containers (just as an example) and make them model the Boost.Range Concepts non-intrusively. Of course, the std:: algorithms do not directly work with ranges, and this particular design issue is handled by the function to extract the iterator. The std:: containers do use the const_iterator and iterator types and have appropriate begin and end overloads. The Range Concepts very closely follow the design of the standard containers in this respect. There does remain a design choice to allow the old Concepts to have combine newly defined Concepts that make the granularity more fine grained. The cost of this, is the large increase in the number of Concepts, and the consequent increase in complexity in formally defining the algorithms. This appears to be a recurring problem with Concept-based design. It seems similar to the issues originally experience with traits where the combination of what were effectively meta-functions was found limiting as the composition was often not correct for different contexts. I believe the general consensus is now to have orthogonal meta-functions, and then it is a matter of debate whether one continues to have traits to combine the meta-functions producing a layered API. I suspect similarly decomposing Concepts into their minimal orthogonal parts results in the most composable design, but at the cost of API specification complexity which consequently results in a steeper learning curve. I'll think about this some more to see if something can be done to make this better while remaining backward compatible. Regards, Neil
participants (2)
-
John
-
Neil Groves