5

Reading this and this and 23.3.6.5/1 of the standard, where in the latest C++ standard draft is it specified that implementers should prioritize the use of non-throwing move-constructor T(T &&t) noexcept over a const copy-constructor T(const T &t) when std::vector<T> re-allocates its element as a result of a push_back operation? Is it 13.3.3.1.4/1 on overload resolution of reference binding?

EDIT 1

I argue on 13.3.3.1.4/1 because of the following reasons:

  • 13.3/2

    Overload resolution selects the function to call in seven distinct contexts within the language. [...] invocation of a constructor for direct-initialization (8.5) of a class object (13.3.1.3); [...] Each of these contexts defines the set of candidate functions and the list of arguments in its own unique way. But, once the candidate functions and argument lists have been identified, the selection of the best function is the same in all cases: First, a subset of the candidate functions (those that have the proper number of arguments and meet certain other conditions) is selected to form a set of viable functions (13.3.2). Then the best viable function is selected based on the implicit conversion sequences (13.3.3.1) needed to match each argument to the corresponding parameter of each viable function.

  • 13.3.2/1

    From the set of candidate functions constructed for a given context (13.3.1), a set of viable functions is chosen, from which the best function will be selected by comparing argument conversion sequences for the best fit (13.3.3). The selection of viable functions considers relationships between arguments and function parameters other than the ranking of conversion sequences.

  • 13.3.1.3/1

    When objects of class type are direct-initialized (8.5), or copy-initialized from an expression of the same or a derived class type (8.5), overload resolution selects the constructor.

  • 13.3.3.1/5

    For the case where the parameter type is a reference, see 13.3.3.1.4.

  • 13.3.3.1.4/1

    When a parameter of reference type binds directly (8.5.3) to an argument expression, the implicit conversion sequence is the identity conversion, [...]

And therefore, I conclude that the requirement of identity conversion results in requiring the prioritization of T(T &&t) noexcept over T(const T &t). But, the other party I am arguing with is not convinced. So, I ask here.

EDIT 2

The following is the link between 23.3.6.5 and 13.3.3.1.4:

First, 23.3.6.5 requires the following of std::vector:

[...] void push_back(const T& x); void push_back(T&& x); Remarks: [...] If an exception is thrown other than by the copy constructor, move constructor, assignment operator, or move assignment operator of T or by any InputIterator operation there are no effects. [...]

That is covered by 23.3.6.1/2 that requires the following:

A vector satisfies all of the requirements of a container and of a reversible container (given in two tables in 23.2), [...]

That is, std::vector is expected to comply with 23.2.1/7 that specifies the following:

Unless otherwise specified, all containers defined in this clause obtain memory using an allocator (see 17.6.3.5).

and with 23.2.1/3 that specifies the following:

For the components affected by this subclause that declare an allocator_type, objects stored in these components shall be constructed using the allocator_traits<allocator_type>::construct function and destroyed using the allocator_traits<allocator_type>::destroy function (20.7.8.2). These functions are called only for the container’s element type, not for internal types used by the container.

Since 20.7.8.2 specifies only one construct function as follows:

template <class T, class... Args> static void construct(Alloc& a, T* p, Args&&... args); 

with 20.7.8.2/5 requiring the following:

Effects: calls a.construct(p, std::forward<Args>(args)...) if that call is well-formed; otherwise, invokes ::new (static_cast<void*>(p)) T(std::forward<Args>(args)...).

and 20.7.9.2/12 requiring the following of the default allocator:

Effects: ::new((void *)p) U(std::forward<Args>(args)...)

, both T(std::forward<Args>(args)...) in 20.7.8.2/5 and U(std::forward<Args>(args)...) in 20.7.9.2/12 will follow the constructor overload resolution requirement in 13.3.3.1.4/1.

The standard therefore requires the implementers to prioritize the use of move-constructor T(T &&t) noexcept over copy-constructor T(const T &t) when std::vector<T> re-allocates its elements due to push_back operations.

12
  • 1
    Unfortunately I think the answer is no. It's left up to implementors what to do; including pure copy. For non-copy insertable types, of course move is required. Commented Sep 25, 2017 at 16:38
  • @AndyG: Why 13.3.3.1.4/1 is not an obligation to prioritize the move-constructor over the copy-constructor? Commented Sep 25, 2017 at 16:41
  • 3
    You might want to paste relevant text here. Following 5 links around isn't all that fun Commented Sep 25, 2017 at 17:12
  • 1
    Since it uses perfect forwarding, construct(Alloc& a, T* p, Args&&... args) could be tricked into calling either a copy or a move constructor. It all depends on whether an lvalue or an rvalue reference is passed for args. That's kind of the point of perfect forwarding. Note that Args&& is not an rvalue reference - it's a forwarding reference, and can bind either way. Commented Sep 25, 2017 at 21:53
  • 1
    @AndyG Your comment gives me the idea of forcing the implementation to use the nonthrowing move-constructor by deleting my const copy-constructor and use std::vector operations that only require MoveInsertable. Commented Sep 25, 2017 at 23:12

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.