diff --git a/doc/issues.html b/doc/issues.html new file mode 100755 index 0000000..eb95f5d --- /dev/null +++ b/doc/issues.html @@ -0,0 +1,442 @@ + + + +
+ + +Author: | David Abrahams | +
---|---|
Contact: | dave@boost-consulting.com | +
Organization: | Boost Consulting | +
date: | $Date$ | +
Copyright: | Copyright David Abrahams 2003. Use, modification and +distribution is subject to the Boost Software License, +Version 1.0. (See accompanying file LICENSE_1_0.txt or copy +at http://www.boost.org/LICENSE_1_0.txt) | +
Several issues with N1550 (New Iterator Concepts) were raised in +the run-up before the fall 2003 C++ Committee meeting, in a thread +beginning with John Maddock's posting c++std-lib-12187. In +looking at those issues, several other problems came up. This +document addresses those issues and discusses some potential +solutions and their impact on N1530 (Iterator Facade and Adaptor).
+The proposed iterator_tag class template accepts an "access +bits" parameter which includes a bit to indicate the iterator's +lvalueness (whether its dereference operator returns a reference +to its value_type. The relevant part of N1550 says:
++The purpose of the lvalue_iterator part of the +iterator_access enum is to communicate to iterator_tag +whether the reference type is an lvalue so that the appropriate +old category can be chosen for the base class. The +lvalue_iterator bit is not recorded in the +iterator_tag::access data member.+
The lvalue_iterator bit is not recorded because N1550 aims to +improve orthogonality of the iterator concepts, and a new-style +iterator's lvalueness is detectable by examining its reference +type. This inside/outside difference is awkward and confusing.
+Shortly after N1550 was accepted, we discovered that an iterator's +lvalueness can be determined knowing only its value_type. This +predicate can be calculated even for old-style iterators (on whose +reference type the standard places few requirements). A trait +in the Boost iterator library does it by relying on the compiler's +unwillingness to bind an rvalue to a T& function template +parameter. Similarly, it is possible to detect an iterator's +readability knowing only its value_type. Thus, any interface +which asks the user to explicitly describe an iterator's +lvalue-ness or readability seems to introduce needless complexity.
+The part of the is_writable_iterator trait definition which +applies to old-style iterators is:
++if (cat is convertible to output_iterator_tag) + return true; + else if ( + cat is convertible to forward_iterator_tag + and iterator_traits<Iterator>::reference is a + mutable reference) + return true; + else + return false; ++
The current forward iterator requirements place no constraints on +the iterator's reference type, so the logic above will give +false negatives for some otherwise-writable forward iterators whose +reference type is not a mutable reference. Also, it will +report false positives for any forward, bidirectional, or random +access iterator whose reference is a mutable reference but +whose value_type is not assignable.
+Similarly, the part of is_swappable_iterator which applies to +old-style iterators is:
++else if (cat is convertible to forward_iterator_tag) { + if (iterator_traits<Iterator>::reference is a const reference) + return false; + else + return true; +} else + return false; ++
In this case false positives are possible for non-writable forward +iterators whose reference type is not a reference, or as above, +any forward, bidirectional, or random access iterator whose +reference is not a constant reference but whose value_type +is not assignable (e.g., because it has a private assignment +operator).
+False negatives can be "reasoned away": since it is part of a +writable iterator's concept definition that +is_writable<I>::value is true, any iterator for which +it is false is by definition not writable. This seems like a +perverse use of logic, though.
+It might be reasonable to conclude that it is a defect that the +standard allows forward iterators with a reference type other +than value_type cv&, but that still leaves the problem +of old-style iterators whose value_type is not assignable. It +is not possible to correctly compute writability and swappability +for those old-style iterators without intervention +(specializations of is_writable_iterator and +is_swappable_iterator) from a user.
+is_swappable_iterator<I> is supposed to yield true if +iter_swap(x,y) is valid for instances x and y of type +I. The only argument we have heard for +is_swappable_iterator goes something like this:
++"If is_swappable_iterator yields false, you +could fall back to using copy construction and assignment on +the value_type instead."+
This line of reasoning, however, falls down when closely examined. +To achieve the same effect using copy construction and assignment +on the iterator's value_type, the iterator must be readable and +writable, and its value_type must be copy-constructible. But +then, iter_swap must work in that case, because its default +implementation just calls swap on the dereferenced iterators. +The only purpose for the swappable concept is to represent +iterators which do not fulfill the properties listed above, but +which are nonetheless swappable because the user has provided an +overload or specialization of iter_swap. In other words, +generic code which wants to swap the referents of two iterators +should always call iter_swap instead of doing the +assignments.
+Try to imagine a case where is_writable_iterator can be used to +choose behavior. Since the only requirement on a writable iterator +is that we can assign into its referent, the only use for +is_writable_iterator in selecting behavior is to modify a +sequence when the sequence is mutable, and to not modify it +otherwise.
+There is no precedent for generic functions which modify their +arguments only if the arguments are non-const reference, and with +good reason: the simple fact that data is mutable does not mean +that a user intends it to be mutated. We provide const and +non-const overloads for functions like operator[], but +these do not modify data; they merely return a reference to data +which preserves the object's mutability properties. We can do the +same with iterators using their reference types; the +accessibility of an assignment operator on the value_type, +which determines writability, does not change that.
+The one plausible argument we can imagine for +is_writable_iterator and is_swappable_iterator is to remove +algorithms from an overload set using a SFINAE technique like +enable_if, but that seems to be too small a gain for the +requirements imposed on iterator implementors by the need to report +writability and swappability, especially since it can't be done +correctly for all existing iterators.
+(incomplete)
+Change iterator_traits as follows:
++iterator_traits<I>::iterator_category + = if (I::iterator_category is a type) // use mpl::has_xxx (SFINAE) + return I::iterator_category + + if (iterator_value_type<I>::type is void + || iterator_difference_type<I>::type is void + ) + return std::output_iterator_tag + + t = iterator_traversal<I>::type + + if (is_lvalue_iterator<I>::value) + { + if (t is convertible to random_access_traversal_tag) + return std::random_access_iterator_tag + if (t is convertible to bidirectional_traversal_tag) + return std::bidirectional_iterator_tag + else if (t is convertible to forward_traversal_tag) + return std::forward_iterator_tag + } + + if (t is convertible to single_pass_traversal_tag + && is_readable_iterator<I>::value + ) + return input_output_iterator_tag // (**) + else + return std::output_iterator_tag ++