Skip to content

<vector>: Iterator range constructor does not enforce implicit conversion requirement #107

Description

Describe the bug
The standard states in the requirements for the constructors of 26.2.3 Sequence containers [sequence.reqmts] §3

In Tables 87 and 88, `X` denotes a sequence container class,
`a` denotes a value of type `X` containing elements of type `T`,
`u` denotes the name of a variable being declared,
`A` denotes `X::allocator_type` if the qualified-id `X::allocator_type` is valid and denotes a type and `allocator<T>` if it doesn’t,
`i` and `j` denote iterators satisfying input iterator requirements and refer to elements implicitly convertible to `value_type`, `[i, j)` denotes a valid range, 
`il` designates an object of type `initializer_list<value_type>`, `n` denotes a value of type `X::size_type`, `p` denotes a valid constant iterator to `a`,
`q` denotes a valid dereferenceable constant iterator to `a`,
`[q1, q2)` denotes a valid range of constant iterators in `a`,
`t`denotes an lvalue or a const rvalue of `X::value_type`, and
`rv` denotes a non-const rvalue of `X::value_type`.
`Args` denotes a template parameter pack;
`args` denotes a function parameter pack with the pattern `Args&&`.

Importantly it requires that the elements behind input iterators i and j are implicitly convertible to value_type. However, it seems that this requirement is ignored. See the following minimal example:

#include <vector>

struct foo {
    explicit foo(int b) : bar(b) {}
    int bar;
};

int main() {
    std::vector<int> input{1, 2, 3, 4, 5};
    std::vector<foo> output(input.begin(), input.end());
}

Expected behavior
Similar to the equivalent code snippet

#include <algorithm>
#include <vector>

struct foo {
    explicit foo(int b) : bar(b) {}
    int bar;
};

int main() {
    std::vector<int> input{1, 2, 3, 4, 5};
    std::vector<foo> output;
    std::copy(input.begin(), input.end(), std::back_inserter(output));
}

there should be a diagnostic about the missing conversion

Additional context
The culprit seem to be _Range_construct_or_tidy that utilizes emplace_back.

I believe that calling push_back instead would enforce the implicit conversion requirement

Activity

  1. changed the title [-]<vector>: Iterator range constructor violates implicit conversion requirement[/-] [+]<vector>: Iterator range constructor does not enforce implicit conversion requirement[/+] on Sep 17, 2019
  2. CaseyCarter commented on Sep 17, 2019

    @CaseyCarter
    Contributor

    The vast majority of requirements that "foo be implicitly convertible to bar" in the C++ Standard are defects since the library almost never performs implicit conversions; this occurrence is no exception to the rule. The sequence container requirements use i and j in three different places:

    • The range constructors X(i, j) and X u(i, j) you mention, which require that the container's value type is Cpp17EmplaceConstructible into the container (implicit conversion is neither necessary nor sufficient) [Aside: The semantic requirements are insufficient to support the "Constructs a sequence container equal to the range [i, j)" effect - the implicit conversion requirement doesn't help here.]

    • The range insert overload a.insert(p, i, j) which also requires Cpp17EmplaceConstructible (again, implicit conversion is neither necessary nor sufficient)

    • The range assign overload a.assign(i, j) which requires both Cpp17EmplaceConstructible as above and that it can assign the result of dereferencing an iterator directly to the container's value type (again, implicit conversion wouldn't be useful here).

    The proper fix here isn't to enforce the unnecessary requirement, it's to strike it from the C++ Standard.

  3. CaseyCarter commented on Sep 17, 2019

    @CaseyCarter
    Contributor

    I've submitted a request to open an LWG issue to correct this wording defect, I'll followup here with the issue number when one is assigned.

  4. miscco commented on Sep 18, 2019

    @miscco
    ContributorAuthor

    Yeah I thought so too, as all mayor Libraries do the same. Nevertheless strange that it is -still- in the standard

  5. miscco commented on Sep 18, 2019

    @miscco
    ContributorAuthor

    What is strange is that the range constructor has no the requirement on Cpp17EmplaceConstructible . In contrast see vector.cons, where the sized constructors have an Requires clause of either Cpp17DefaultInsertable or Cpp17CopyInsertable

    Maybe one should add

    §9 Requires: value_type shall be Cpp17EmplaceConstructible from *first.

  6. CaseyCarter commented on Sep 18, 2019

    @CaseyCarter
    Contributor

    What is strange is that the range constructor has no the requirement on Cpp17EmplaceConstructible.

    The requirement is present for those constructors, it's over in the sequence container requirements table.

  7. miscco commented on Sep 18, 2019

    @miscco
    ContributorAuthor

    Grml, why is the Requires clause mentioned explicitely again for vector(n, t) but not vector(i, j)?

    I am generally against repeating oneself in a specification but that seems like a valid exception.

  8. CaseyCarter commented on Sep 23, 2019

    @CaseyCarter
    Contributor

    I've submitted a request to open an LWG issue to correct this wording defect, I'll followup here with the issue number when one is assigned.

    This is now LWG 3297. Thanks for the report!

  9. added
    fixedSomething works now, yay!
    LWG issue neededA wording defect that should be submitted to LWG as a new issue
    and removed
    LWG issue neededA wording defect that should be submitted to LWG as a new issue
    fixedSomething works now, yay!
    on Sep 24, 2019
  10. jwakely commented on Oct 21, 2019

    @jwakely

    there should be a diagnostic about the missing conversion

    Ignoring the fact this requirement is bogus anyway, the library is under no obligation to diagnose such mistakes by users. Failing to meet that requirement results in undefined behaviour, and compiling it without complaint is a valid implementation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    LWG issue neededA wording defect that should be submitted to LWG as a new issueresolvedSuccessfully resolved without a commit

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions