Revised 2014-05-24 at 10:05:55 UTC

Tentative Issues


2106. move_iterator wrapping iterators returning prvalues

Section: 24.5.3 [move.iterators] Status: Tentatively Ready Submitter: Dave Abrahams Opened: 2011-11-30 Last modified: 2014-05-23

View all other issues in [move.iterators].

View all issues with Tentatively Ready status.

Discussion:

Shouldn't move_iterator be specialized so that if the iterator it wraps returns a prvalue when dereferenced, the move_iterator also returns by value? Otherwise, it creates a dangling reference.

Howard: I believe just changing move_iterator<I>::reference would do. A direction might be testing on is_reference<iterator_traits<I>::reference>, or is_reference<decltype(*declval<I>())>.

Daniel: I would prefer to use a consistent style among the iterator adaptors, so I suggest to keep with the iterator_traits typedefs if possible.

using reference = typename conditional<
  is_reference<typename iterator_traits<Iterator>::reference>::value,
  value_type&&,
  value_type
>::type;

We might also want to ensure that if Iterator's reference type is a reference, the referent is equal to value_type (after removal of cv-qualifiers). In general we have no such guarantee.

Marc: In the default case where we don't return value_type&&, should we use value_type or should we keep the reference type of the wrapped iterator?

Daniel: This suggestion looks appealing at first, but the problem here is that using this typedef can make it impossible for move_iterator to satisfy its contract, which means returning an rvalue of the value type (Currently it says rvalue-reference, but this must be fixed as of this issue anyway). I think that user-code can reasonably expect that when it has constructed an object m of move_iterator<It>, where It is a valid mutable iterator type, the expression

It::value_type&& rv = *m;

is well-formed.

Let's set R equal to iterator_traits<Iterator>::reference in the following. We can discuss the following situations:

  1. R is a reference type: We can only return the corresponding xvalue of R, if value_type is reference-related to the referent type, else this is presumably no forward iterator and we cannot say much about it, except that it must be convertible to value_type, so it better should return a prvalue.
  2. R is not a reference type: In this case we can rely on a conversion to value_type again, but not much more. Assume we would return R directly, this might turn out to have a conversion to an lvalue-reference type of the value type (for example). If that is the case, this would indirectly violate the contract of move_iterator.

In regard to the first scenario I suggest that implementations are simply required to check that V2 = remove_cv<remove_reference<R>::type>::type is equal to the value type V1 as a criterion to return this reference as an xvalue, in all other cases it should return the value type directly as prvalue.

The additional advantage of this strategy is, that we always ensure that reference has the correct cv-qualification, if R is a real reference.

It is possible to improve this a bit by indeed supporting reference-related types, this would require to test is_same<V1, V2>::value || is_base_of<V1, V2>::value instead. I'm unsure whether (a) this additional effort is worth it and (b) a strict reading of the forward iterator requirements seems not to allow to return a reference-related type (Whether this is a defect or not is another question).

[2011-12-05: Marc Glisse comments and splits into two resolution alternatives]

I guess I am looking at the speed of:

value_type x;
x = *m;

(copy construction would likely trigger copy elision and thus be neutral) instead of the validity of:

value_type&& x = *m;

In this sense, Daniels earlier proposition that ignored value_type and just did switch_lvalue_ref_to_rvalue_ref<reference> was easier to understand (and it didn't require thinking about reference related types).

The currently proposed resolution has been split into two alternatives.

[2012, Kona]

Move to Review.

Alisdair: This only applies to input iterators, so keep that in mind when thinking about this.

STL: I see what B is doing, but not A.

Howard: I agree.

Alisdair: Should we use add_rvalue_reference?

STL: No, we do not want reference collapsing.

STL: Re A, messing with the CV qualification scares me.

Alisdair: Agree. That would break my intent.

STL: Actually I don't think it's actually wrong, but I still don't see what it's doing.

Alisdair: A is picking the value type, B is picking the proxy type.

Howard: I like returning the proxy type.

STL: Returning a reference (B) seems right, because the requirements say "reference". I suspect that B works correctly if you have a move iterator wrapping a move iterator wrapping a thing. I think that A would mess up the type in the middle.

Considerable discussion about which version is correct, checking various examples.

STL: Still think B is right. Still don't understand A. In A we are losing the proxyness.

Howard: Agree 100%. We don't want to lose the proxy. If it's const, so be it.

STL: B is also understandable by mortals.

Howard: Remove to review, keep A but move it out of the proposed resolution area (but keep it for rational).

Alisdair: Adding an explanatory note might be a good idea, if someone wants to write one.

Walter: Concerned about losing the word "reference" in p.1.

Howard: move_iterator will return an xvalue or a prvalue, both of which are rvalues.

[Proposed resolution A, rejected in preference to the currently proposed resolution (B)

  1. Change 24.5.3 [move.iterators] p1 as indicated:

    Class template move_iterator is an iterator adaptor with the same behavior as the underlying iterator except that its dereference operator implicitly converts the value returned by the underlying iterator's dereference operator to an rvalue referenceof the value type. Some generic algorithms can be called with move iterators to replace copying with moving.

  2. Change 24.5.3.1 [move.iterator], class template move_iterator synopsis, as indicated:

    namespace std {
      template <class Iterator>
      class move_iterator {
      public:
        typedef Iterator iterator_type;
        typedef typename iterator_traits<Iterator>::difference_type difference_type;
        typedef Iterator pointer;
        typedef typename iterator_traits<Iterator>::value_type value_type;
        typedef typename iterator_traits<Iterator>::iterator_category iterator_category;
        typedef value_type&&see below reference;
        […]
      };
    }
    
  3. Immediately following the class template move_iterator synopsis in 24.5.3.1 [move.iterator] insert a new paragraph as indicated:

    -?- Let R be iterator_traits<Iterator>::reference and let V be iterator_traits<Iterator>::value_type. If is_reference<R>::value is true and if remove_cv<remove_reference<R>::type>::type is the same type as V, the template instantiation move_iterator<Iterator> shall define the nested type named reference as a synonym for remove_reference<R>::type&&, otherwise as a synonym for V.

]

[2012, Portland: Move to Tentatively Ready]

AJM wonders if the implied trait might be useful elsewhere, and worth adding to type traits as a transformation type trait.

Suspicion that the Range SG might find such a trait useful, but wait until there is clear additional use of such a trait before standardizing.

Minor wording tweak to use add_rvalue_reference rather than manually adding the &&, then move to Tentatively Ready.

[2013-01-09 Howard Hinnant comments]

I believe the P/R for LWG 2106 is incorrect (item 3). The way it currently reads, move_iterator<I>::reference is always an lvalue reference. I.e. if R is an lvalue reference type, then reference becomes add_rvalue_reference<R>::type which is just R. And if R is not a reference type, then reference becomes R (which is also just R ;-)).

I believe the correct wording is what was there previously:

-?- Let R be iterator_traits<Iterator>::reference. If is_reference<R>::value is true, the template instantiation move_iterator<Iterator> shall define the nested type named reference as a synonym for remove_reference<R>::type&&, otherwise as a synonym for R.

Additionally Marc Glisse points out that move_iterator<I>::operator*() should return static_cast<reference>(*current), not std::move(*current).

Previous resolution:

This wording is relative to the FDIS.

  1. Change 24.5.3 [move.iterators] p1 as indicated:

    Class template move_iterator is an iterator adaptor with the same behavior as the underlying iterator except that its dereference operator implicitly converts the value returned by the underlying iterator's dereference operator to an rvalue reference. Some generic algorithms can be called with move iterators to replace copying with moving.

  2. Change 24.5.3.1 [move.iterator], class template move_iterator synopsis, as indicated:

    namespace std {
      template <class Iterator>
      class move_iterator {
      public:
        typedef Iterator iterator_type;
        typedef typename iterator_traits<Iterator>::difference_type difference_type;
        typedef Iterator pointer;
        typedef typename iterator_traits<Iterator>::value_type value_type;
        typedef typename iterator_traits<Iterator>::iterator_category iterator_category;
        typedef value_type&&see below reference;
        […]
      };
    }
    
  3. Immediately following the class template move_iterator synopsis in 24.5.3.1 [move.iterator] insert a new paragraph as indicated:

    -?- Let R be iterator_traits<Iterator>::reference. If is_reference<R>::value is true, the template instantiation move_iterator<Iterator> shall define the nested type named reference as a synonym for add_rvalue_reference<R>::type, otherwise as a synonym for R.

[2014-05-19, Daniel comments]

The term instantiation has been changed to specialization in the newly added paragraph as suggested by STL and much preferred by myself.

[2014-05-19 Library reflector vote]

The issue has been identified as Tentatively Ready based on five votes in favour.

Proposed resolution:

This wording is relative to N3936.

  1. Change 24.5.3 [move.iterators] p1 as indicated:

    Class template move_iterator is an iterator adaptor with the same behavior as the underlying iterator except that its indirection operator implicitly converts the value returned by the underlying iterator's indirection operator to an rvalue reference. Some generic algorithms can be called with move iterators to replace copying with moving.

  2. Change 24.5.3.1 [move.iterator], class template move_iterator synopsis, as indicated:

    namespace std {
      template <class Iterator>
      class move_iterator {
      public:
        typedef Iterator iterator_type;
        typedef typename iterator_traits<Iterator>::difference_type difference_type;
        typedef Iterator pointer;
        typedef typename iterator_traits<Iterator>::value_type value_type;
        typedef typename iterator_traits<Iterator>::iterator_category iterator_category;
        typedef value_type&&see below reference;
        […]
      };
    }
    
  3. Immediately following the class template move_iterator synopsis in 24.5.3.1 [move.iterator] insert a new paragraph as indicated:

    -?- Let R be iterator_traits<Iterator>::reference. If is_reference<R>::value is true, the template specialization move_iterator<Iterator> shall define the nested type named reference as a synonym for remove_reference<R>::type&&, otherwise as a synonym for R.

  4. Edit 24.5.3.3.4 [move.iter.op.star] p1 as indicated:

    reference operator*() const;
    

    -1- Returns: std::movestatic_cast<reference>(*current).


2129. User specializations of std::initializer_list

Section: 17.6.4.2.1 [namespace.std], 18.9 [support.initlist] Status: Tentatively Ready Submitter: Richard Smith Opened: 2012-01-18 Last modified: 2014-05-02

View other active issues in [namespace.std].

View all other issues in [namespace.std].

View all issues with Tentatively Ready status.

Discussion:

Since the implementation is intended to magically synthesize instances of std::initializer_list (rather than by a constructor call, for instance), user specializations of this type can't generally be made to work. I can't find any wording which makes such specializations ill-formed, though, which leads me to suspect that they're technically legal under the provisions of 17.6.4.2.1 [namespace.std] p1.

[2012, Kona]

This sounds correct, but we need wording for a resolution.

Marshall Clow volunteers to produce wording.

[2014-02-19, Jonathan Wakely provides proposed wording]

[2014-03-27, Library reflector vote]

The issue has been identified as Tentatively Ready based on six votes in favour.

Proposed resolution:

This wording is relative to N3936.

  1. Add new new paragraph below 18.9 [support.initlist] p2:

    -2- An object of type initializer_list<E> provides access to an array of objects of type const E. […]

    -?- If an explicit specialization or partial specialization of initializer_list is declared, the program is ill-formed.


2212. tuple_size for const pair request <tuple> header

Section: 20.2 [utility] Status: Tentatively Ready Submitter: Alisdair Meredith Opened: 2012-11-09 Last modified: 2014-05-02

View all other issues in [utility].

View all issues with Tentatively Ready status.

Discussion:

The <utility> header declares sufficient of the tuple API to specialize the necessary templates for pair, notably tuple_size and tuple_element. However, it does not make available the partial specializations that support cv-qualified template arguments, so while I can write the following after including only <utility>:

#include <utility>

using TestType = std::pair<int, int>;
static_assert(2 == std::tuple_size<TestType>(), "Pairs have two elements");
std::tuple_element<0, TestType>::type var{1};

the following may fail to compile unless I also include <tuple>:

#include <utility>

using TestType = const std::pair<int, int>;
static_assert(2 == std::tuple_size<TestType>(), "Pairs have two elements");
std::tuple_element<0, TestType>::type var{1};

Note, however, that the latter may compile with some standard library implementations but not others, leading to subtle portability issues.

[2013-03-15 Issues Teleconference]

Moved to Open.

Howard notes that we have the same issue with array, so any resolution should apply to that header too.

[2013-10-18 Daniel provides wording]

The suggested wording uses a similar approach as we already have in 24.7 [iterator.range] to ensure that the range access templates are available when at least one of an enumerated list of header files is included.

I also think that the restricted focus on tuple_size of this issue is too narrow and should be extended to the similar partial template specializations of tuple_element as well. Therefore the suggested wording ensures this as well.

[2014-03-27 Library reflector vote]

The issue has been identified as Tentatively Ready based on eight votes in favour.

Proposed resolution:

This wording is relative to N3936.

  1. Change 20.4.2.5 [tuple.helper] as indicated:

    template <class T> class tuple_size<const T>;
    template <class T> class tuple_size<volatile T>;
    template <class T> class tuple_size<const volatile T>;
    

    -3- Let TS denote tuple_size<T> of the cv-unqualified type T. Then each of the three templates shall meet the UnaryTypeTrait requirements (20.10.1) with a BaseCharacteristic of

    integral_constant<size_t, TS::value>
    

    -?- In addition to being available via inclusion of the <tuple> header, each of the three templates are available when any of the headers <array> or <utility> are included.

    template <size_t I, class T> class tuple_element<I, const T>;
    template <size_t I, class T> class tuple_element<I, volatile T>;
    template <size_t I, class T> class tuple_element<I, const volatile T>;
    

    -?- Let TE denote tuple_element<I, T> of the cv-unqualified type T. Then each of the three templates shall meet the TransformationTrait requirements (20.10.1) with a member typedef type that names the following type:

    • for the first specialization, add_const<TE::type>::type,

    • for the second specialization, add_volatile<TE::type>::type, and

    • for the third specialization, add_cv<TE::type>::type.

    -?- In addition to being available via inclusion of the <tuple> header, each of the three templates are available when any of the headers <array> or <utility> are included.


2217. operator==(sub_match, string) slices on embedded '\0's

Section: 28.9.2 [re.submatch.op] Status: Tentatively Ready Submitter: Jeffrey Yasskin Opened: 2012-11-26 Last modified: 2014-05-02

View all other issues in [re.submatch.op].

View all issues with Tentatively Ready status.

Discussion:

template <class BiIter, class ST, class SA>
  bool operator==(
    const basic_string<
      typename iterator_traits<BiIter>::value_type, ST, SA>& lhs,
    const sub_match<BiIter>& rhs);

is specified as:

Returns: rhs.compare(lhs.c_str()) == 0.

This is odd because sub_match::compare(basic_string) is defined to honor embedded '\0' characters. This could allow a sub_match to == or != a std::string unexpectedly.

[Daniel:]

This wording change was done intentionally as of LWG 1181, but the here mentioned slicing effect was not considered at that time. It seems best to use another overload of compare to fix this problem:

Returns: rhs.str().compare(0, rhs.length(), lhs.data(), lhs.size()) == 0.

or

Returns: rhs.compare(sub_match<BiIter>::string_type(lhs.data(), lhs.size())) == 0.

[2013-10-17: Daniel provides concrete wording]

The original wording was suggested to reduce the need to allocate memory during comparisons. The specification would be very much easier, if sub_match would provide an additional compare overload of the form:

int compare(const value_type* s, size_t n) const;

But given the fact that currently all of basic_string's compare overloads are defined in terms of temporary string constructions, the following proposed wording does follow the same string-construction route as basic_string does (where needed to fix the embedded zeros issue) and to hope that existing implementations ignore to interpret this semantics in the literal sense.

I decided to use the second replacement form

Returns: rhs.compare(sub_match<BiIter>::string_type(lhs.data(), lhs.size())) == 0.

because it already reflects the existing style used in 28.9.2 [re.submatch.op] p31.

[2014-02-15 post-Issaquah session : move to Tentatively Ready]

Proposed resolution:

This wording is relative to N3691.

  1. Change 28.9.2 [re.submatch.op] as indicated:

    template <class BiIter, class ST, class SA>
      bool operator==(
        const basic_string<
          typename iterator_traits<BiIter>::value_type, ST, SA>& lhs,
        const sub_match<BiIter>& rhs);
    

    -7- Returns: rhs.compare(lhs.c_str()typename sub_match<BiIter>::string_type(lhs.data(), lhs.size())) == 0.

    […]

    template <class BiIter, class ST, class SA>
      bool operator<(
        const basic_string<
          typename iterator_traits<BiIter>::value_type, ST, SA>& lhs,
        const sub_match<BiIter>& rhs);
    

    -9- Returns: rhs.compare(lhs.c_str()typename sub_match<BiIter>::string_type(lhs.data(), lhs.size())) > 0.

    […]

    template <class BiIter, class ST, class SA>
      bool operator==(const sub_match<BiIter>& lhs,
                      const basic_string<
                        typename iterator_traits<BiIter>::value_type, ST, SA>& rhs);
    

    -13- Returns: lhs.compare(rhs.c_str()typename sub_match<BiIter>::string_type(rhs.data(), rhs.size())) == 0.

    […]

    template <class BiIter, class ST, class SA>
      bool operator<(const sub_match<BiIter>& lhs,
                     const basic_string<
                       typename iterator_traits<BiIter>::value_type, ST, SA>& rhs);
    

    -15- Returns: lhs.compare(rhs.c_str()typename sub_match<BiIter>::string_type(rhs.data(), rhs.size())) < 0.


2230. "see below" for initializer-list constructors of unordered containers

Section: 23.5 [unord] Status: Tentatively Ready Submitter: Jonathan Wakely Opened: 2013-01-06 Last modified: 2014-05-23

View all other issues in [unord].

View all issues with Tentatively Ready status.

Discussion:

The unordered_map class definition in 23.5.4.1 [unord.map.overview] declares an initializer-list constructor that says "see below":

unordered_map(initializer_list<value_type>,
    size_type = see below,
    const hasher& hf = hasher(),
    const key_equal& eql = key_equal(),
    const allocator_type& a = allocator_type());

But that constructor isn't defined below. The same problem exists for the other unordered associative containers.

[2013-09 Chicago]

STL: ordered are also missing declarations, but issue is forthcoming

Walter: how does adding a signature address issue? — nevermind

Jayson: in his wording, isn't he just dropping the size_type?

Walter: partial fix is to introduce the name

Stefanus: explanation of requiring name because of n buckets

STL: solution for his issue satisfies both ordered and unordered and is simplier than provided wording

STL: patches general table instead

STL: proposes adding extra rows instead of extra declarations

Stefanus: clarify n in the synopsis

Walter: general rule, name is optional in declaration

Stefanus: how to proceed

Walter: significant overlap with forthcoming issue, suggestion to defer

[2014-02-20 Re-open Deferred issues as Priority 4]

[2014-03-27 Jonathan improves proposed wording]

[2014-05-20 STL and Jonathan communicate]

STL: With 2322 resolved, is there anything left for this issue to fix?

Jonathan: The synopsis still says "see below" and it's not immediately clear that "see below" means "see the definition of a different constructor, which defines the behaviour of this one due to a table defined much earlier".

[2014-05-23 Library reflector vote]

The issue has been identified as Tentatively Ready based on five votes in favour.

Proposed resolution:

This wording is relative to N3936.

  1. Edit 23.5.4.1 [unord.map.overview], class template unordered_map synopsis, as follows:

    […]
    unordered_map(initializer_list<value_type> il,
      size_type n = see below,
      const hasher& hf = hasher(),
      const key_equal& eql = key_equal(),
      const allocator_type& a = allocator_type());
    […]
    
  2. Edit 23.5.4.2 [unord.map.cnstr] as follows:

    template <class InputIterator>
    unordered_map(InputIterator f, InputIterator l,
      size_type n = see below,
      const hasher& hf = hasher(),
      const key_equal& eql = key_equal(),
      const allocator_type& a = allocator_type());
    unordered_map(initializer_list<value_type> il,
      size_type n = see below,
      const hasher& hf = hasher(),
      const key_equal& eql = key_equal(),
      const allocator_type& a = allocator_type());
    

    -3- Effects: Constructs an empty unordered_map using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is implementation-defined. Then inserts elements from the range [f, l) for the first form, or from the range [il.begin(), il.end()) for the second form. max_load_factor() returns 1.0.

  3. Edit 23.5.5.1 [unord.multimap.overview], class template unordered_multimap synopsis, as follows:

    […]
    unordered_multimap(initializer_list<value_type> il,
      size_type n = see below,
      const hasher& hf = hasher(),
      const key_equal& eql = key_equal(),
      const allocator_type& a = allocator_type());
    […]
    
  4. Edit 23.5.5.2 [unord.multimap.cnstr] as follows:

    template <class InputIterator>
    unordered_multimap(InputIterator f, InputIterator l,
      size_type n = see below,
      const hasher& hf = hasher(),
      const key_equal& eql = key_equal(),
      const allocator_type& a = allocator_type());
    unordered_multimap(initializer_list<value_type> il,
      size_type n = see below,
      const hasher& hf = hasher(),
      const key_equal& eql = key_equal(),
      const allocator_type& a = allocator_type());
    

    -3- Effects: Constructs an empty unordered_multimap using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is implementation-defined. Then inserts elements from the range [f, l) for the first form, or from the range [il.begin(), il.end()) for the second form. max_load_factor() returns 1.0.

  5. Edit 23.5.6.1 [unord.set.overview], class template unordered_set synopsis, as follows:

    […]
    unordered_set(initializer_list<value_type> il,
      size_type n = see below,
      const hasher& hf = hasher(),
      const key_equal& eql = key_equal(),
      const allocator_type& a = allocator_type());
    […]
    
  6. Edit 23.5.6.2 [unord.set.cnstr] as follows:

    template <class InputIterator>
    unordered_set(InputIterator f, InputIterator l,
      size_type n = see below,
      const hasher& hf = hasher(),
      const key_equal& eql = key_equal(),
      const allocator_type& a = allocator_type());
    unordered_set(initializer_list<value_type> il,
      size_type n = see below,
      const hasher& hf = hasher(),
      const key_equal& eql = key_equal(),
      const allocator_type& a = allocator_type());
    

    -3- Effects: Constructs an empty unordered_set using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is implementation-defined. Then inserts elements from the range [f, l) for the first form, or from the range [il.begin(), il.end()) for the second form. max_load_factor() returns 1.0.

  7. Edit 23.5.7.1 [unord.multiset.overview], class template unordered_multiset synopsis, as follows:

    […]
    unordered_multiset(initializer_list<value_type> il,
      size_type n = see below,
      const hasher& hf = hasher(),
      const key_equal& eql = key_equal(),
      const allocator_type& a = allocator_type());
    […]
    
  8. Edit 23.5.7.2 [unord.multiset.cnstr] as follows:

    template <class InputIterator>
    unordered_multiset(InputIterator f, InputIterator l,
      size_type n = see below,
      const hasher& hf = hasher(),
      const key_equal& eql = key_equal(),
      const allocator_type& a = allocator_type());
    unordered_multiset(initializer_list<value_type> il,
      size_type n = see below,
      const hasher& hf = hasher(),
      const key_equal& eql = key_equal(),
      const allocator_type& a = allocator_type());
    

    -3- Effects: Constructs an empty unordered_multiset using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is implementation-defined. Then inserts elements from the range [f, l) for the first form, or from the range [il.begin(), il.end()) for the second form. max_load_factor() returns 1.0.


2233. bad_function_call::what() unhelpful

Section: 20.9.11.1 [func.wrap.badcall] Status: Tentatively Ready Submitter: Jonathan Wakely Opened: 2013-01-05 Last modified: 2014-05-02

View all issues with Tentatively Ready status.

Discussion:

A strict reading of the standard implies std::bad_function_call{}.what() returns the same string as std::exception{}.what() which doesn't help to know what happened if you catch an exception by reference to std::exception.

For consistency with bad_weak_ptr::what() it should return "bad_function_call".

See c++std-lib-33515 for other details.

There was a considerable support on the reflector to instead change the specification of both bad_weak_ptr::what() and bad_function_call::what() to return an implementation-defined string instead.

[2013-03-15 Issues Teleconference]

Moved to Open.

Consensus that we want consistency in how this is treated. Less consensus on what the common direction should be.

Alisdair to provide wording proposing that all string literals held by standard exception objects are either unspecified, or implmentation defined.

[2014-02-15 Issauqah]

STL: I think it should be an implementation-defined NTBS, same on bad_weak_ptr. I will write a PR.

[2014-03-27, STL provides improved wording]

The new wording reflects better the general agreement of the committee, see also issue 2376 for similar wording.

[2014-03-28 Library reflector vote]

The issue has been identified as Tentatively Ready based on five votes in favour.

Proposed resolution:

This wording is relative to N3936.

  1. Edit 20.9.11.1.1 [func.wrap.badcall.const]:

    bad_function_call() noexcept;
    

    -1- Effects: constructs a bad_function_call object.

    -?- Postconditions: what() returns an implementation-defined NTBS.


2266. vector and deque have incorrect insert requirements

Section: 23.2.3 [sequence.reqmts] Status: Tentatively Ready Submitter: Ahmed Charles Opened: 2013-05-17 Last modified: 2014-05-23

View other active issues in [sequence.reqmts].

View all other issues in [sequence.reqmts].

View all issues with Tentatively Ready status.

Discussion:

According to Table 100 in n3485 23.2.3 [sequence.reqmts]/4 the notes for the expression a.insert(p,i,j) say:

Requires: T shall be EmplaceConstructible into X from *i. For vector, if the iterator does not meet the forward iterator requirements (24.2.5), T shall also be MoveInsertable into X and MoveAssignable.

Each iterator in the range [i,j) shall be dereferenced exactly once.

pre: i and j are not iterators into a.

Inserts copies of elements in [i, j) before p

There are two problems with that wording: First, the special constraints for vector, that are expressed to be valid for forward iterators only, are necessary for all iterator categories. Second, the same special constraints are needed for deque, too.

[2013-10-05, Stephan T. Lavavej comments and provides alternative wording]

In Chicago, we determined that the original proposed resolution was correct, except that it needed additional requirements. When vector insert(p, i, j) is called with input-only iterators, it can't know how many elements will be inserted, which is obviously problematic for insertion anywhere other than at the end. Therefore, implementations typically append elements (geometrically reallocating), followed by rotate(). Given forward+ iterators, some implementations append and rotate() when they determine that there is sufficient capacity. Additionally, deque insert(p, i, j) is typically implemented with prepending/appending, with a possible call to reverse(), followed by a call to rotate(). Note that rotate()'s requirements are strictly stronger than reverse()'s.

Therefore, when patching Table 100, we need to add rotate()'s requirements. Note that this does not physically affect code (implementations were already calling rotate() here), and even in Standardese terms it is barely noticeable — if an element is MoveInsertable and MoveAssignable then it is almost certainly MoveConstructible and swappable. However, this patch is necessary to be strictly correct.

Previous resolution from Ahmed Charles:

  1. Change Table 100 as indicated:

    Table 100 — Sequence container requirements (in addition to container) (continued)
    Expression Return type Assertion/note pre-/post-condition
    a.insert(p,i,j) iterator Requires: T shall be EmplaceConstructible into X from *i. For vector and deque, if the iterator does not meet the forward iterator requirements (24.2.5), T shall also be MoveInsertable into X and MoveAssignable.
    Each iterator in the range [i,j) shall be dereferenced exactly once.
    pre: i and j are not iterators into a.
    Inserts copies of elements in [i, j) before p

[2014-02-15 post-Issaquah session : move to Tentatively Ready]

Pablo: We might have gone too far with the fine-grained requirements. Typically these things come in groups.

Alisdair: I think the concepts folks assumed we would take their guidance.

Move to Tentatively Ready.

Proposed resolution:

  1. Change Table 100 as indicated:

    Table 100 — Sequence container requirements (in addition to container) (continued)
    Expression Return type Assertion/note pre-/post-condition
    a.insert(p,i,j) iterator Requires: T shall be EmplaceConstructible into X
    from *i. For vector and deque, if the iterator
    does not meet the forward iterator requirements (24.2.5), T shall also be
    MoveInsertable into X, MoveConstructible,
    and MoveAssignable, and swappable (17.6.3.2 [swappable.requirements]).
    Each iterator in the range [i,j) shall be dereferenced exactly once.
    pre: i and j are not iterators into a.
    Inserts copies of elements in [i, j) before p

2302. Passing null pointer to placement new

Section: 18.6.1.3 [new.delete.placement] Status: Tentatively NAD Submitter: Marc Glisse Opened: 2013-09-12 Last modified: 2014-05-02

View other active issues in [new.delete.placement].

View all other issues in [new.delete.placement].

Discussion:

Based on this discussion and as discussed in c++std-core-23998 and c++std-lib-34442, calling placement new currently forces the compiler to check if the pointer is null before initializing the object (a non-negligible cost). It seems many people were not aware of this and they consider it a user error to pass a null pointer to it.

Proposed resolution: for operator new and operator new[], add:

Requires: ptr shall not be a null pointer.

[2014-02-15 post-Issaquah session : move to Tentatively NAD]

AJM to supply the rationale...

Proposed resolution:

This wording is relative to N3691.

  1. Change 18.6.1.3 [new.delete.placement] as indicated:

    void* operator new(std::size_t size, void* ptr) noexcept;
    

    -?- Requires: ptr shall not be a null pointer.

    -2- Returns: ptr.

    -3- Remarks: Intentionally performs no other action.

    -4- [Example: This can be useful for constructing an object at a known address:

    void* place = operator new(sizeof(Something));
    Something* p = new (place) Something();
    

    end example]

    void* operator new[](std::size_t size, void* ptr) noexcept;
    

    -?- Requires: ptr shall not be a null pointer.

    -5- Returns: ptr.

    -6- Remarks: Intentionally performs no other action.


2361. Apply 2299 resolution throughout library

Section: 20.8.1.2 [unique.ptr.single], 20.7.3.1 [pointer.traits.types], 20.7.7.1 [allocator.uses.trait], 20.7.8.1 [allocator.traits.types], 23.2.3 [sequence.reqmts] Status: Tentatively Ready Submitter: Jonathan Wakely Opened: 2014-02-14 Last modified: 2014-05-23

View other active issues in [unique.ptr.single].

View all other issues in [unique.ptr.single].

View all issues with Tentatively Ready status.

Discussion:

LWG 2299 addressed a N.B. comment pointing out that recently added wording about a type existing was not clear what happens if the type exists but is inaccessible. There are 16 pre-existing uses of the same language in the library that should use the same wording used to resolve 2299.

The relevant paragraphs are:

20.8.1.2 [unique.ptr.single]

20.7.3.1 [pointer.traits.types]

20.7.7.1 [allocator.uses.trait]

20.7.8.1 [allocator.traits.types]

23.2.3 [sequence.reqmts]

[2014-05-16, Daniel provides wording]

[2014-05-18 Library reflector vote]

The issue has been identified as Tentatively Ready based on six votes in favour.

Proposed resolution:

This wording is relative to N3936.

  1. Change 20.7.3.1 [pointer.traits.types] as indicated:

    typedef see below element_type;
    

    -1- Type: Ptr::element_type if such a type existsthe qualified-id Ptr::element_type is valid and denotes a type (14.8.2 [temp.deduct]); otherwise, T if Ptr is a class template instantiation of the form SomePointer<T, Args>, where Args is zero or more type arguments; otherwise, the specialization is ill-formed.

    typedef see below difference_type;
    

    -2- Type: Ptr::difference_type if such a type existsthe qualified-id Ptr::difference_type is valid and denotes a type (14.8.2 [temp.deduct]); otherwise, std::ptrdiff_t.

    template <class U> using rebind = see below;
    

    -3- Alias template: Ptr::rebind<U> if such a type existsthe qualified-id Ptr::rebind<U> is valid and denotes a type (14.8.2 [temp.deduct]); otherwise, SomePointer<U, Args> if Ptr is a class template instantiation of the form SomePointer<T, Args>, where Args is zero or more type arguments; otherwise, the instantiation of rebind is ill-formed.

  2. Change 20.7.7.1 [allocator.uses.trait] p1 as indicated:

    template <class T, class Alloc> struct uses_allocator;
    

    -1- Remarks: automatically detects whether T has a nested allocator_type that is convertible from Alloc. Meets the BinaryTypeTrait requirements (20.10.1). The implementation shall provide a definition that is derived from true_type if a typethe qualified-id T::allocator_type existsis valid and denotes a type (14.8.2 [temp.deduct]) and is_convertible<Alloc, T::allocator_type>::value != false, otherwise it shall be derived from false_type. […]

  3. Change 20.7.8.1 [allocator.traits.types] as indicated:

    typedef see below pointer;
    

    -1- Type: Alloc::pointer if such a type existsthe qualified-id Alloc::pointer is valid and denotes a type (14.8.2 [temp.deduct]); otherwise, value_type*.

    typedef see below const_pointer;
    

    -2- Type: Alloc::const_pointer if such a type existsthe qualified-id Alloc::const_pointer is valid and denotes a type (14.8.2 [temp.deduct]); otherwise, pointer_traits<pointer>::rebind<const value_type>.

    typedef see below void_pointer;
    

    -3- Type: Alloc::void_pointer if such a type existsthe qualified-id Alloc::void_pointer is valid and denotes a type (14.8.2 [temp.deduct]); otherwise, pointer_traits<pointer>::rebind<void>.

    typedef see below const_void_pointer;
    

    -4- Type: Alloc::const_void_pointer if such a type existsthe qualified-id Alloc::const_void_pointer is valid and denotes a type (14.8.2 [temp.deduct]); otherwise, pointer_traits<pointer>::rebind<const void>.

    typedef see below difference_type;
    

    -5- Type: Alloc::difference_type if such a type existsthe qualified-id Alloc::difference_type is valid and denotes a type (14.8.2 [temp.deduct]); otherwise, pointer_traits<pointer>::difference_type.

    typedef see below size_type;
    

    -6- Type: Alloc::size_type if such a type existsthe qualified-id Alloc::size_type is valid and denotes a type (14.8.2 [temp.deduct]); otherwise, make_unsigned_t<difference_type>.

    typedef see below propagate_on_container_copy_assignment;
    

    -7- Type: Alloc::propagate_on_container_copy_assignment if such a type existsthe qualified-id Alloc::propagate_on_container_copy_assignment is valid and denotes a type (14.8.2 [temp.deduct]); otherwise, false_type.

    typedef see below propagate_on_container_move_assignment;
    

    -8- Type: Alloc::propagate_on_container_move_assignment if such a type existsthe qualified-id Alloc::propagate_on_container_move_assignment is valid and denotes a type (14.8.2 [temp.deduct]); otherwise, false_type.

    typedef see below propagate_on_container_swap;
    

    -9- Type: Alloc::propagate_on_container_swap if such a type existsthe qualified-id Alloc::propagate_on_container_swap is valid and denotes a type (14.8.2 [temp.deduct]); otherwise, false_type.

    template <class T> using rebind_alloc = see below;
    

    -10- Alias template: Alloc::rebind<T>::other if such a type existsthe qualified-id Alloc::rebind<T>::other is valid and denotes a type (14.8.2 [temp.deduct]); otherwise, Alloc<T, Args> if Alloc is a class template instantiation of the form Alloc<U, Args>, where Args is zero or more type arguments; otherwise, the instantiation of rebind_alloc is ill-formed.

  4. Change 20.8.1.2 [unique.ptr.single] p3 as indicated:

    -3- If the typequalified-id remove_reference_t<D>::pointer existsis valid and denotes a type (14.8.2 [temp.deduct]), then unique_ptr<T, D>::pointer shall be a synonym for remove_reference_t<D>::pointer. […]

  5. Change 23.2.3 [sequence.reqmts] p3 as indicated:

    -3- In Tables 100 and 101, X denotes a sequence container class, a denotes a value of X containing elements of type T, A denotes X::allocator_type if it existsthe qualified-id X::allocator_type is valid and denotes a type (14.8.2 [temp.deduct]) and std::allocator<T> if it doesn't, […]


2365. Missing noexcept in shared_ptr::shared_ptr(nullptr_t)

Section: 20.8.2.2 [util.smartptr.shared] Status: Tentatively Ready Submitter: Cassio Neri Opened: 2014-02-13 Last modified: 2014-05-02

View all other issues in [util.smartptr.shared].

View all issues with Tentatively Ready status.

Discussion:

The declaration and definition of shared_ptr::shared_ptr(nullptr_t), given in 20.8.2.2 [util.smartptr.shared], is

constexpr shared_ptr(nullptr_t) : shared_ptr() { }

The intention seems clear: this constructor should have the same semantics of the default constructor. However, contrarily to the default constructor, this one is not noexcept. In contrast, unique_ptr::unique_ptr(nullptr_t) is noexcept, as per 20.8.1.2 [unique.ptr.single]:

constexpr unique_ptr(nullptr_t) noexcept : unique_ptr() { }

Both libstdc++ and libc++ have added noexcept to shared_ptr::shared_ptr(nullptr_t). Microsoft's STL has not.

[2014-03-26 Library reflector vote]

The issue has been identified as Tentatively Ready based on six votes in favour.

Proposed resolution:

This wording is relative to N3936.

  1. Change class template shared_ptr synopsis, 20.8.2.2 [util.smartptr.shared], as indicated:

    constexpr shared_ptr(nullptr_t) noexcept : shared_ptr() { }
    

2376. bad_weak_ptr::what() overspecified

Section: 20.8.2.1 [util.smartptr.weakptr] Status: Tentatively Ready Submitter: Jonathan Wakely Opened: 2014-03-27 Last modified: 2014-05-02

View all issues with Tentatively Ready status.

Discussion:

20.8.2.1 [util.smartptr.weakptr] p2 requires bad_weak_ptr to return precisely the string "bad_weak_ptr".

There was general agreement on the reflector and at the Issaquah meeting that this is over-constrained and implementation should be free to return something more descriptive if desired.

The proposed resolution makes bad_weak_ptr consistent with other exception types such as bad_alloc and bad_cast.

If accepted, the P/R for issue 2233, which currently uses similar wording to bad_weak_ptr, could be updated appropriately.

[2014-03-27 Library reflector vote]

The issue has been identified as Tentatively Ready based on six votes in favour.

Proposed resolution:

This wording is relative to N3936.

  1. Edit 20.8.2.1 [util.smartptr.weakptr]:

    bad_weak_ptr() noexcept;
    

    -2- Postconditions: what() returns "bad_weak_ptr"an implementation-defined NTBS.