Doc. no. N3788
Date: 2013-09-27
Project: Programming Language C++
Reply to: Alisdair Meredith <lwgchair@gmail.com>

Revised 2013-09-27 at 16:09:08 UTC

Immediate Issues


2013. Do library implementers have the freedom to add constexpr?

Section: 17.6.5.6 [constexpr.functions] Status: Immediate Submitter: Matt Austern Opened: 2010-11-12 Last modified: 2013-09-27

View all issues with Immediate status.

Discussion:

Suppose that a particular function is not tagged as constexpr in the standard, but that, in some particular implementation, it is possible to write it within the constexpr constraints. If an implementer tags such a function as constexpr, is that a violation of the standard or is it a conforming extension?

There are two questions to consider. First, is this allowed under the as-if rule? Second, if it does not fall under as-if, is there (and should there be) any special license granted to implementers to do this anyway, sort of the way we allow elision of copy constructors even though it is detectable by users?

I believe that this does not fall under "as-if", so implementers probably don't have that freedom today. I suggest changing the WP to grant it. Even if we decide otherwise, however, I suggest that we make it explicit.

[ 2011 Bloomington ]

General surprise this was not already in 'Ready' status, and so moved.

[ 2012 Kona ]

Some concern expressed when presented to full committee for the vote to WP status that this issue had been resolved without sufficient thought of the consequences for diverging library implementations, as users may use SFINAE to observe different behavior from otherwise identical code. Issue moved back to Review status, and will be discussed again in Portland with a larger group. Note for Portland: John Spicer has agreed to represent Core's concerns during any such discussion within LWG.

[2013-09 Chicago]

Straw poll: LWG strongly favoured to remove from implementations the freedom to add constexpr.

Matt provides new wording.

[2013-09 Chicago]

Move to Immediate after reviewing Matt's new wording.

Proposed resolution:

In 17.6.5.6 [constexpr.functions], change paragraph 1 to:

This standard explicitly requires that certain standard library functions are constexpr [dcl.constexpr]. An implementation shall not declare any standard library function signature as constexpr except for those where it is explicitly required. Within any header that provides any non-defining declarations of constexpr functions or constructors an implementation shall provide corresponding definitions.


2018. [CD] regex_traits::isctype Returns clause is wrong

Section: 28.7 [re.traits] Status: Immediate Submitter: Jonathan Wakely Opened: 2010-11-16 Last modified: 2013-09-27

View other active issues in [re.traits].

View all other issues in [re.traits].

View all issues with Immediate status.

Discussion:

Addresses GB 10

28.7 [re.traits] p. 12 says:

returns true if f bitwise or'ed with the result of calling lookup_classname with an iterator pair that designates the character sequence "w" is not equal to 0 and c == '_'

If the bitmask value corresponding to "w" has a non-zero value (which it must do) then the bitwise or with any value is also non-zero, and so isctype('_', f) returns true for any f. Obviously this is wrong, since '_' is not in every ctype category.

There's a similar problem with the following phrases discussing the "blank" char class.

[2011-05-06: Jonathan Wakely comments and provides suggested wording]

DR 2019 added isblank support to <locale> which simplifies the definition of regex_traits::isctype by removing the special case for the "blank" class.

My suggestion for 2018 is to add a new table replacing the lists of recognized names in the Remarks clause of regex_traits::lookup_classname. I then refer to that table in the Returns clause of regex_traits::isctype to expand on the "in an unspecified manner" wording which is too vague. The conversion can now be described using the "is set" term defined by 17.5.2.1.3 [bitmask.types] and the new table to convey the intented relationship between e.g. [[:digit:]] and ctype_base::digit, which is not actually stated in the FDIS.

The effects of isctype can then most easily be described in code, given an "exposition only" function prototype to do the not-quite-so-unspecified conversion from char_class_type to ctype_base::mask.

The core of LWG 2018 is the "bitwise or'ed" wording which gives the wrong result, always evaluating to true for all values of f. That is replaced by the condition (f&x) == x where x is the result of calling lookup_classname with "w". I believe that's necessary, because the "w" class could be implemented by an internal "underscore" class i.e. x = _Alnum|_Underscore in which case (f&x) != 0 would give the wrong result when f==_Alnum.

The proposed resolution also makes use of ctype::widen which addresses the problem that the current wording only talks about "w" and '_' which assumes charT is char. There's still room for improvement here: the regex grammar in 28.13 [re.grammar] says that the class names in the table should always be recognized, implying that e.g. U"digit" should be recognized by regex_traits<char32_t>, but the specification of regex_traits::lookup_classname doesn't cover that, only mentioning char and wchar_t. Maybe the table should not distinguish narrow and wide strings, but should just have one column and add wording to say that regex_traits widens the name as if by using use_facet<ctype<charT>>::widen().

Another possible improvement would be to allow additional implementation-defined extensions in isctype. An implementation is allowed to support additional class names in lookup_classname, e.g. [[:octdigit:]] for [0-7] or [[:bindigit:]] for [01], but the current definition of isctype provides no way to use them unless ctype_base::mask also supports them.

[2011-05-10: Alberto and Daniel perform minor fixes in the P/R]

[ 2011 Bloomington ]

Consensus that this looks to be a correct solution, and the presentation as a table is a big improvement.

Concern that the middle section wording is a little muddled and confusing, Stefanus volunteered to reword.

[ 2013-09 Chicago ]

Stefanus provides improved wording (replaced below)

[ 2013-09 Chicago ]

Move as Immediate after reviewing Stefanus's revised wording.

Proposed resolution:

This wording is relative to the FDIS.

  1. Modify 28.7 [re.traits] p. 10 as indicated:

    template <class ForwardIterator>
      char_class_type lookup_classname(
        ForwardIterator first, ForwardIterator last, bool icase = false) const;
    

    -9- Returns: an unspecified value that represents the character classification named by the character sequence designated by the iterator range [first,last). If the parameter icase is true then the returned mask identifies the character classification without regard to the case of the characters being matched, otherwise it does honor the case of the characters being matched.(footnote 335) The value returned shall be independent of the case of the characters in the character sequence. If the name is not recognized then returns a value that compares equal to 0.

    -10- Remarks: For regex_traits<char>, at least the names "d", "w", "s", "alnum", "alpha", "blank", "cntrl", "digit", "graph", "lower", "print", "punct", "space", "upper" and "xdigit"narrow character names in Table X shall be recognized. For regex_traits<wchar_t>, at least the names L"d", L"w", L"s", L"alnum", L"alpha", L"blank", L"cntrl", L"digit", L"graph", L"lower", L"print", L"punct", L"space", L"upper" and L"xdigit"wide character names in Table X shall be recognized.

  2. Modify 28.7 [re.traits] p. 12 as indicated:

    bool isctype(charT c, char_class_type f) const;
    

    -11- Effects: Determines if the character c is a member of the character classification represented by f.

    -12- Returns: Converts f into a value m of type std::ctype_base::mask in an unspecified manner, and returns true if use_facet<ctype<charT> >(getloc()).is(m, c) is true. Otherwise returns true if f bitwise or'ed with the result of calling lookup_classname with an iterator pair that designates the character sequence "w" is not equal to 0 and c == '_', or if f bitwise or'ed with the result of calling lookup_classname with an iterator pair that designates the character sequence "blank" is not equal to 0 and c is one of an implementation-defined subset of the characters for which isspace(c, getloc()) returns true, otherwise returns false. Given an exposition-only function prototype

    
      template<class C>
       ctype_base::mask convert(typename regex_traits<C>::char_class_type f);
    
    

    that returns a value in which each ctype_base::mask value corresponding to a value in f named in Table X is set, then the result is determined as if by:

    
    ctype_base::mask m = convert<charT>(f);
    const ctype<charT>& ct = use_facet<ctype<charT>>(getloc());
    if (ct.is(m, c)) {
      return true;
    } else if (c == ct.widen('_')) {
      charT w[1] = { ct.widen('w') };
      char_class_type x = lookup_classname(w, w+1);
      
      return (f&x) == x;
    } else {
      return false;
    } 
    
    

    [Example:

    
    regex_traits<char> t;
    string d("d");
    string u("upper");
    regex_traits<char>::char_class_type f;
    f = t.lookup_classname(d.begin(), d.end());
    f |= t.lookup_classname(u.begin(), u.end());
    ctype_base::mask m = convert<char>(f); // m == ctype_base::digit|ctype_base::upper
    

    end example]

    [Example:

    
    regex_traits<char> t;
    string w("w");
    regex_traits<char>::char_class_type f;
    f = t.lookup_classname(w.begin(), w.end());
    t.isctype('A', f); // returns true
    t.isctype('_', f); // returns true
    t.isctype(' ', f); // returns false
    

    end example]

  3. At the end of 28.7 [re.traits] add a new "Table X — Character class names and corresponding ctype masks":

    Table X — Character class names and corresponding ctype masks
    Narrow character name Wide character name Corresponding ctype_base::mask value
    "alnum" L"alnum" ctype_base::alnum
    "alpha" L"alpha" ctype_base::alpha
    "blank" L"blank" ctype_base::blank
    "cntrl" L"cntrl" ctype_base::cntrl
    "digit" L"digit" ctype_base::digit
    "d" L"d" ctype_base::digit
    "graph" L"graph" ctype_base::graph
    "lower" L"lower" ctype_base::lower
    "print" L"print" ctype_base::print
    "punct" L"punct" ctype_base::punct
    "space" L"space" ctype_base::space
    "s" L"s" ctype_base::space
    "upper" L"upper" ctype_base::upper
    "w" L"w" ctype_base::alnum
    "xdigit" L"xdigit" ctype_base::xdigit

2078. Throw specification of async() incomplete

Section: 30.6.8 [futures.async] Status: Immediate Submitter: Nicolai Josuttis Opened: 2011-08-29 Last modified: 2013-09-27

View other active issues in [futures.async].

View all other issues in [futures.async].

View all issues with Immediate status.

Discussion:

The current throw specification of async() does state:

-6- Throws: system_error if policy is launch::async and the implementation is unable to start a new thread.

First it seems not clear whether this only applies if policy equals launch::async of if the async launch mode flag is set (if policy|launch::async!=0)

In the discussion Lawrence Crowl also wrote:

More generally, I think what we want to say is that if the implementation cannot successfully execute on one of the policies allowed, then it must choose another. The principle would apply to implementation-defined policies as well.

Peter Sommerlad:

Should not throw. That was the intent. "is async" meat exactly.

[2012, Portland: move to Tentatively NAD Editorial]

If no launch policy, it is undefined behavior.

Agree with Lawrence, should try all the allowed policies. We will rephrase so that the policy argument should be lauch::async. Current wording seems good enough.

We believe this choice of policy statement is really an editorial issue.

Proposed resolution:

This wording is relative to N3691.

  1. Change 30.6.8 [futures.async] p6, p7 as indicated:

    -6- Throws: system_error if policy is == launch::async and the implementation is unable to start a new thread.

    -7- Error conditions:


2097. packaged_task constructors should be constrained

Section: 30.6.9.1 [futures.task.members] Status: Immediate Submitter: Jonathan Wakely Opened: 2011-11-02 Last modified: 2013-09-25

View other active issues in [futures.task.members].

View all other issues in [futures.task.members].

View all issues with Immediate status.

Discussion:

With the proposed resolution of 2067, this no longer selects the copy constructor:

std::packaged_task<void()> p1;
std::packaged_task<void()> p2(p1);

Instead this constructor is a better match:

template <class F>
 explicit packaged_task(F&& f);

This attempts to package a packaged_task, which internally tries to copy p2, which fails because the copy constructor is deleted. For at least one implementation the resulting error message is much less helpful than the expected "cannot call deleted function" because it happens after instantiating several more templates rather than in the context where the constructor is called.

I believe the solution is to constrain to the template constructors so the template argument F cannot be deduced as (possibly cv) packaged_task& or packaged_task. It could be argued this constraint is already implied because packaged_task is not copyable and the template constructors require that "invoking a copy of f shall behave the same as invoking f".

Daniel points out that the variadic constructor of std::thread described in 30.3.1.2 [thread.thread.constr] has a similar problem and suggests a similar wording change, which has been integrated below.

An alternative is to declare thread(thread&) and packaged_task(packaged_task&) as deleted.

[2012, Portland]

This issue appears to be more about library specification than technical concurrency issues, so should be handled in LWG.

[2012, Portland]

Move to Immediate resolution.

Howard volunteered existing implementation experience with the first change, and saw no issue that the second would introduce any new issue.

Proposed resolution:

This wording is relative to the FDIS.

  1. Insert a new Remarks element to 30.3.1.2 [thread.thread.constr] around p3 as indicated:

    template <class F, class ...Args> explicit thread(F&& f, Args&&... args);
    

    -3- Requires: F and each Ti in Args shall satisfy the MoveConstructible requirements. INVOKE(DECAY_COPY ( std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...) (20.8.2) shall be a valid expression.

    -?- Remarks: This constructor shall not participate in overload resolution if decay<F>::type is the same type as std::thread.

  2. Insert a new Remarks element to 30.6.9.1 [futures.task.members] around p2 as indicated:

    template <class F>
      packaged_task(F&& f);
    template <class F, class Allocator>
      explicit packaged_task(allocator_arg_t, const Allocator& a, F&& f);
    

    -2- Requires: INVOKE(f, t1, t2, ..., tN, R), where t1, t2, ..., tN are values of the corresponding types in ArgTypes..., shall be a valid expression. Invoking a copy of f shall behave the same as invoking f.

    -?- Remarks: These constructors shall not participate in overload resolution if decay<F>::type is the same type as std::packaged_task<R(ArgTypes...)>.


2159. atomic_flag initialization

Section: 29.7 [atomics.flag] Status: Immediate Submitter: Alberto Ganesh Barbati Opened: 2012-05-24 Last modified: 2013-09-27

View all other issues in [atomics.flag].

View all issues with Immediate status.

Discussion:

29.7 [atomics.flag]/4 describes the ATOMIC_FLAG_INIT, but it's not quite clear about a couple of points:

  1. it's said that ATOMIC_FLAG_INIT "can be used to initialize an object of type atomic_flag" and the following example:

    std::atomic_flag guard = ATOMIC_FLAG_INIT;
    

    is presented. It's not clear whether the macro can also be used in the other initialization contexts:

    std::atomic_flag guard ATOMIC_FLAG_INIT; 
    std::atomic_flag guard {ATOMIC_FLAG_INIT};
    
    struct A { std::atomic_flag flag; A(); };
    A::A() : flag (ATOMIC_FLAG_INIT); 
    A::A() : flag {ATOMIC_FLAG_INIT};
    

    Please also note that examples are non-normative, according to the ISO directives, meaning that the wording presents no normative way to use the macro.

  2. it's said that "It is unspecified whether an uninitialized atomic_flag object has an initial state of set or clear.". I believe the use of "uninitialized" is inappropriate. First of all, if an object is uninitialized it is obvious that we cannot assert anything about its state. Secondly, it doesn't address the following cases:

    std::atomic_flag a; // object is "initialized" by trivial default constructor
    std::atomic_flag a {}; // object is value-initialized
    static std::atomic_flag a; // object is zero-initialized
    

    strictly speaking a trivial constructor "initializes" the object, although it doesn't actually initialize the sub-objects.

  3. it's said that "For a static-duration object, that initialization shall be static.". Considering the following example:

    struct A
    {
      A(); // user-provided, not constexpr
    
      std::atomic_flag flag = ATOMIC_FLAG_INIT;
      // possibly other non-static data members
    };
    
    static A a;
    

    The object a.flag (as a sub-object of the object a) has static-duration, yet the initialization has to be dynamic because A::A is not constexpr.

[2012, Portland]

We would like to be able to allow more initialisation contexts for example:

  1. C struct
  2. C++ constructor initializer-list

However we need further input from experts with implementation specific knowledge to identify which additional contexts (if any) would be universally valid.

Moved to open

[2012, Portland]

Move to Immediate, following review.

Some discussion over the explicit use of only copy initialization, and not direct initializtion. This is necessary to allow the implementation of atomic_flag as an aggregate, and may be further reviewed in the future.

Proposed resolution:

[This wording is relative to N3376.]

Change 29.7 [atomics.flag]/4 as follows:

The macro ATOMIC_FLAG_INIT shall be defined in such a way that it can be used to initialize an object of type atomic_flag to the clear state. The macro can be used in the form:

atomic_flag guard = ATOMIC_FLAG_INIT;

It is unspecified whether the macro can be used in other initialization contexts. For a complete static-duration object, that initialization shall be static. It is unspecified whether an uninitialized Unless initialized with ATOMIC_FLAG_INIT, it is unspecified whether an atomic_flag object has an initial state of set or clear. [ Example:

atomic_flag guard = ATOMIC_FLAG_INIT;

end example ]


2275. [CD] Why is forward_as_tuple not constexpr?

Section: 20.4.2.4 [tuple.creation] Status: Immediate Submitter: Marshall Clow Opened: 2013-07-30 Last modified: 2013-09-25

View all other issues in [tuple.creation].

View all issues with Immediate status.

Discussion:

Addresses ES 11

In n3471, a bunch of routines from header <tuple> were made constexpr.

make_tuple/tuple_cat/get<>(tuple)/relational operators — all these were "constexpr-ified".

But not forward_as_tuple.

Why not?

This was discussed in Portland, and STL opined that this was "an omission" (along with tuple_cat, which was added)

In discussion on c++std-lib@accu.org list, Pablo agreed that forward_as_tuple should be constexpr.

[2013-09 Chicago]

Moved to Immediate, this directly addresses an NB comment and the wording is non-controversial.

Proposed resolution:

This wording is relative to N3691.

  1. Change header <tuple> synopsis, 20.4.1 [tuple.general] as indicated:

    template <class... Types>
      constexpr tuple<Types&&...> forward_as_tuple(Types&&...) noexcept;
    
  2. Change 20.4.2.4 [tuple.creation] before p5 as indicated:

    template <class... Types>
      constexpr tuple<Types&&...> forward_as_tuple(Types&&... t) noexcept;
    

2284. Inconsistency in allocator_traits::max_size

Section: 20.8.8 [allocator.traits] Status: Immediate Submitter: Marshall Clow Opened: 2013-08-27 Last modified: 2013-09-26

View all issues with Immediate status.

Discussion:

Section 20.8.8 [allocator.traits] says:

static size_type max_size(const Alloc& a) noexcept;

Section 20.8.8.2 [allocator.traits.members] says:

static size_type max_size(Alloc& a) noexcept;

These should be the same.

Discussion:

Pablo (who I believe wrote the allocator_traits proposal) says "The function should take a const reference."

Proposed resolution:

This wording is relative to N3691.

  1. Change 20.8.8.2 [allocator.traits.members] as follows:

    static size_type max_size(const Alloc& a) noexcept;
    

2298. [CD] is_nothrow_constructible is always false because of create<>

Section: 20.11.4.3 [meta.unary.prop] Status: Immediate Submitter: Daniel Krügler Opened: 2013-09-24 Last modified: 2013-09-26

View other active issues in [meta.unary.prop].

View all other issues in [meta.unary.prop].

View all issues with Immediate status.

Discussion:

Addresses US 18

The trait is_constructible<T, Args...> is defined in terms of a helper template, create<>, that is identical to std::declval<> except for the latter's noexcept clause.

If the absence of noexcept is critical to this definition, insert a Note of explanation; otherwise, excise create<> and reformulate in terms of declval<> the definition of is_constructible.

[2013-09-24 Daniel comments and provides resolution suggestion]

Replacing create<> by std::declval<> would make the situation worse, because the definition of is_constructible is based on a well-formed variable definition and there is no way to specify a variable definition without odr-using its initializer arguments. It should also be added, that there is another problem with the specification of all existing is_trivially_* traits, because neither create<> nor std::declval<> are considered as trivial functions, but this should be solved by a different issue.

[2013-09-26 Nico improves wording]

The additional change is just to keep both places were create() is defined consistent.

Proposed resolution:

This wording is relative to N3691.

  1. Change 20.11.4.3 [meta.unary.prop] around p6 as indicated:

    -6- Given the following function prototype:

    template <class T>
      typename add_rvalue_reference<T>::type create() noexcept;
    

    the predicate condition for a template specialization is_constructible<T, Args...> shall be satisfied if and only if the following variable definition would be well-formed for some invented variable t:

    T t(create<Args>()...);
    

    […]

  2. Change 20.11.4.3 [meta.unary.prop] around p4 as indicated:

    -4- Given the following function prototype:

    template <class T>
      typename add_rvalue_reference<T>::type create() noexcept;
    

    the predicate condition for a template specialization is_convertible<From, To> shall be satisfied if and only if the return expression in the following code would be well-formed, including any implicit conversions to the return type of the function:

    To test() {
      return create<From>();
    }
    

    […]


2300. [CD] Redundant sections for map and multimap members should be removed

Section: 23.4.4.5 [map.ops], 23.4.5.4 [multimap.ops] Status: Immediate Submitter: Daniel Krügler Opened: 2013-09-25 Last modified: 2013-09-26

View all issues with Immediate status.

Discussion:

Addresses ES 17

Sections are redundant with general associative container requirements at 23.2.4 [associative.reqmts], Table 102.

Suggested action:

Delete sections.

[2013-09-25 Daniel provides resolution suggestion]

[2013-09-25 Chicago]

Daniel's wording is good, move to Immediate to resolve NB comment.

Proposed resolution:

This wording is relative to N3691.

  1. Change the header <map> synopsis, 23.4.4.1 [map.overview] p2 as indicated:

    // 23.4.4.5, map operations:
    iterator find(const key_type& x);
    const_iterator find(const key_type& x) const;
    template <class K> iterator find(const K& x);
    template <class K> const_iterator find(const K& x) const;
    
  2. Delete the complete sub-clause 23.4.4.5 [map.ops]:

    23.4.4.5 map operations [map.ops]

    iterator find(const key_type& x);
    const_iterator find(const key_type& x) const;
    iterator lower_bound(const key_type& x);
    const_iterator lower_bound(const key_type& x) const;
    iterator upper_bound(const key_type& x);
    const_iterator upper_bound(const key_type &x) const;
    pair<iterator, iterator>
      equal_range(const key_type &x);
    pair<const_iterator, const_iterator>
      equal_range(const key_type& x) const;
    

    -1- The find, lower_bound, upper_bound and equal_range member functions each have two versions, one const and the other non-const. In each case the behavior of the two functions is identical except that the const version returns a const_iterator and the non-const version an iterator (23.2.4).

  3. Delete the complete sub-clause 23.4.5.4 [multimap.ops]:

    23.4.5.4 multimap operations [multimap.ops]

    iterator find(const key_type &x);
    const_iterator find(const key_type& x) const;
    
    iterator lower_bound(const key_type& x);
    const_iterator lower_bound(const key_type& x) const;
    
    pair<iterator, iterator>
      equal_range(const key_type &x);
    pair<const_iterator, const_iterator>
      equal_range(const key_type& x) const;
    

    -1- The find, lower_bound, upper_bound and equal_range member functions each have two versions, one const and one non-const. In each case the behavior of the two versions is identical except that the const version returns a const_iterator and the non-const version an iterator (23.2.4).