enable_view
has false positivesSection: 25.4.4 [range.view] Status: C++20 Submitter: Germany Opened: 2019-11-06 Last modified: 2021-02-25
Priority: 0
View all other issues in [range.view].
View all issues with C++20 status.
Discussion:
Addresses DE 282
"Since the difference between range and view is largely semantic, the
two are differentiated with the help of enable_view
." (§3)
enable_view
is designed as on opt-in trait to specify that a type is a
view. It defaults to true for types derived from view_base
(§4.2) which
is clearly a form of opt-in. But it also employs a heuristic assuming that anything with
iterator == const_iterator
is also view (§4.3).
This is a very poor heuristic, the same paragraph already needs to define six
exceptions from this rule for standard library types (§4.2).
Experience in working with range-v3 has revealed multiple of our own library types as being
affected from needing to opt-out from the "auto-opt-in", as well. This is counter-intuitive:
something that was never designed to be a view shouldn't go through hoops so that it isn't
treated as a view.
Proposed change:
Make enable_view
truly be opt-in by relying only on explicit specialisation or
inheritance from view_base
. This means removing 24.4.4 §4.2 - §4.4 and
introducing new §4.2 "Otherwise, false".
basic_string_view
and span
need to opt-in to being a view now.
Casey Carter:
enable_view
(25.4.4 [range.view]) is designed as on opt-in trait to specify that a type
is a view. It defaults to true
for types derived from view_base
— which is a
form of opt-in — and it also employs a heuristic. Unfortunately, the heuristic has false positives.
The working draft itself includes six exceptions to the heuristic for standard library types. Since
false positives are much more problematic for users than false negatives, we should eliminate the heuristic.
[2019-11 Status to Ready during Wednesday night issue processing in Belfast.]
Proposed resolution:
This wording is relative to N4835.
Modify 25.4.4 [range.view] as indicated:
template<class T> inline constexpr bool enable_view =see belowderived_from<T, view_base>;
-4- Remarks: For a typeT
, the default value ofenable_view<T>
is:
(4.1) — Ifderived_from<T, view_base>
istrue
,true
.
(4.2) — Otherwise, ifT
is a specialization of class templateinitializer_list
(17.10 [support.initlist]),set
(23.4.6 [set]),multiset
(23.4.7 [multiset]),unordered_set
(23.5.6 [unord.set]),unordered_multiset
(23.5.7 [unord.multiset]), ormatch_results
(28.6.9 [re.results]),false
.
(4.3) — Otherwise, if bothT
andconst T
modelrange
andrange_reference_t<T>
is not the same type asrange_reference_t<const T>
,false
. [Note: Deepconst
-ness implies element ownership, whereas shallow const-ness implies reference semantics. — end note]
(4.4) — Otherwise, true.
Modify 27.3.2 [string.view.synop], header <string_view>
synopsis, as indicated:
namespace std { // 27.3.3 [string.view.template], class template basic_string_view template<class charT, class traits = char_traits<charT>> class basic_string_view; template<class charT, class traits> inline constexpr bool ranges::enable_view<basic_string_view<charT, traits>> = true; […] }
Modify 23.7.2.1 [span.syn], header <span>
synopsis, as indicated:
namespace std { // constants inline constexpr size_t dynamic_extent = numeric_limits<size_t>::max(); // 23.7.2.2 [views.span], class template span template<class ElementType, size_t Extent = dynamic_extent> class span; template<class ElementType, size_t Extent> inline constexpr bool ranges::enable_view<span<ElementType, Extent>> = Extent == 0 || Extent == dynamic_extent; […] }
Modify [range.split.outer.value], class split_view::outer_iterator::value_type
synopsis, as indicated:
[Drafting note: The following applies the proposed wording for LWG 3276]
namespace std::ranges { template<class V, class Pattern> template<bool Const> struct split_view<V, Pattern>::outer_iterator<Const>::value_type : view_interface<value_type> { private: outer_iterator i_ = outer_iterator(); // exposition only public: value_type() = default; constexpr explicit value_type(outer_iterator i); constexpr inner_iterator<Const> begin() const; constexpr default_sentinel_t end() const; }; }