This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++20 status.

### 3326. `enable_view` has false positives

Section: 26.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".

Double-check if existing standard library types like `basic_string_view` and `span` need to opt-in to being a view now.

Casey Carter:

`enable_view` (26.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.

1. Modify 26.4.4 [range.view] as indicated:

   >     template<class T>
   >       inline constexpr bool enable_view = see belowderived_from<T, view_base>;

   > > -4- Remarks: For a type `T`, the default value of `enable_view<T>` is:

   > > 1. (4.1) — If `derived_from<T, view_base>` is `true`, `true`.

   > > 2. (4.2) — Otherwise, if `T` is a specialization of class template `initializer_list` (17.11 [support.initlist]), `set` (24.4.6 [set]), `multiset` (24.4.7 [multiset]), `unordered_set` (24.5.6 [unord.set]), `unordered_multiset` (24.5.7 [unord.multiset]), or `match_results` (32.9 [re.results]), `false`.

   > > 3. (4.3) — Otherwise, if both `T` and `const T` model `range` and `range_reference_t<T>` is not the same type as `range_reference_t<const T>`, `false`. [Note: Deep `const`-ness implies element ownership, whereas shallow const-ness implies reference semantics. — end note]

   > > 4. (4.4) — Otherwise, true.

2. Modify 23.3.2 [string.view.synop], header `<string_view>` synopsis, as indicated:

   >     namespace std {
   >       // 23.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;

   >       […]
   >     }

3. Modify 24.7.2 [span.syn], header `<span>` synopsis, as indicated:

   >     namespace std {
   >       // constants
   >       inline constexpr size_t dynamic_extent = numeric_limits<size_t>::max();

   >       // 24.7.3 [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;

   >       […]
   >     }

4. Modify 99 [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;
   >       };
   >     }