Skip to content

<xutility>: default_initializable<move_iterator<It>> may be problematic for Clang #3377

Description

@frederick-vs-ja

Describe the bug
TL;DR: LLVM-60293 is likely not a bug and we may need to change move_iterator's default constructor to make Clang happy.

In cases like views::join_with(r1 | views::as_rvalue, r2 | views::as_rvalue) (already occur in test file due to #3359), the iterator type of the resulted join_with_view wraps an move_iterator and default_initializable<move_iterator<It>> is detected, where It is the iterator type of r1. When It is a non-default-initializable input_iterator type (more precisely, when It{} is ill-formed), MSVC STL's implementation of move_iteratorresults in weird behavior of Clang.

MSVC STL's move_iterator has a defaulted default constructor:

_CONSTEXPR17 move_iterator() = default;

And the required value-initialization is performed via default member initializer:
iterator_type _Current{};

Per [class.default.ctor]/2.3, the default member initializer makes the default constructor of move_iterator never deleted (which is decided in CWG-927). Given a non-default-initializable It, default_initializable<move_iterator<It>> ask whether is_constructible_v<move_iterator<It>> is true, which may make the program ill-formed per this note, and implementation divergence seems (unfortunately) permitted (discussed in llvm/llvm-project#60293).

There're many iterator-related facilities using default_initializable to constrain their default constructors. We may need to change move_iterator's default constructor to make them usable with move_iterator and non-default-initializable input iterators when compiling with Clang.

The current approach has some benefits:

  • the exception specification is automatically strengthened;
  • the default constructor can be sometimes implicitly constexpr in C++14 mode, although this makes little sense because most operations are only constexpr since C++17.

Possible resolutions (may be only applied for Clang):

  • Just making the default constructor non-defaulted. Cons:
    • either we need a non-conforming constexpr in C++14 mode, or we'll break some non-portable constexpr uses of move_iterator in C++14;
    • exception specification strengthening may be preferable, but throughput might be (sightly) damaged.
  • Adding base classes to make the default constructor conditionally deleted and otherwise defaulted. Cons:
    • also damages throughput;
    • the adapted iterator may be designed in such way that asking whether it satisfies is_default_constructible is ill-formed, while neither input_iterator nor Cpp17InputIterator cares default-initializability.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcompilerCompiler work involved

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions