This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++23 status.

3525. uses_allocator_construction_args fails to handle types convertible to pair

Section: 20.2.8.2 [allocator.uses.construction] Status: C++23 Submitter: Tim Song Opened: 2021-02-23 Last modified: 2023-11-22

Priority: 3

View all other issues in [allocator.uses.construction].

View all issues with C++23 status.

Discussion:

As currently specified, the following program is ill-formed (and appears to have been since LWG 2975(i)):

struct S {
  operator std::pair<const int, int>() const {
    return {};
  }
};

void f() {
  std::pmr::map<int, int> s;
  s.emplace(S{});
}

There's no matching overload for uses_allocator_construction_args<pair<const int, int>>(alloc, S&&), since S is not a pair and every overload for constructing pairs that takes one non-allocator argument expects a pair from which template arguments can be deduced.

[2021-02-27 Tim adds PR and comments]

The superseded resolution below attempts to solve this issue by adding two additional overloads of uses_allocator_construction_args to handle this case. However, the new overloads forces implicit conversions at the call to uses_allocator_construction_args, which requires the result to be consumed within the same full-expression before any temporary created from the conversion is destroyed. This is not the case for the piecewise_construct overload of uses_allocator_construction_args, which recursively calls uses_allocator_construction_args for the two elements of the pair, which might themselves be pairs.

The approach taken in the revised PR is to produce an exposition-only pair-constructor object instead. The object holds the allocator and the argument by reference, implicitly converts to the specified specialization of pair, and when so converted return a pair that is constructed by uses-allocator construction with the converted value of the original argument. This maintains the existing design that pair itself doesn't know anything about allocator construction.

Previous resolution [SUPERSEDED]:

This wording is relative to N4878.

  1. Edit 20.2.2 [memory.syn], header <memory> synopsis, as indicated:

    namespace std {
      […]
      // 20.2.8.2 [allocator.uses.construction], uses-allocator construction
      […]
    
      template<class T, class Alloc>
        constexpr auto uses_allocator_construction_args(const Alloc& alloc,
                                                        const remove_cv_t<T>& pr) noexcept;
      template<class T, class Alloc, class U, class V>
        constexpr auto uses_allocator_construction_args(const Alloc& alloc,
                                                        const pair<U, V>& pr) noexcept -> see below;
    
      template<class T, class Alloc>
        constexpr auto uses_allocator_construction_args(const Alloc& alloc,
                                                        remove_cv_t<T>&& pr) noexcept;
      template<class T, class Alloc, class U, class V>
        constexpr auto uses_allocator_construction_args(const Alloc& alloc,
                                                        pair<U, V>&& pr) noexcept -> see below;
      […]
    }
    
  2. Edit 20.2.8.2 [allocator.uses.construction] as indicated:

    template<class T, class Alloc>
      constexpr auto uses_allocator_construction_args(const Alloc& alloc,
                                                      const remove_cv_t<T>& pr) noexcept;
    template<class T, class Alloc, class U, class V>
      constexpr auto uses_allocator_construction_args(const Alloc& alloc,
                                                      const pair<U, V>& pr) noexcept -> see below;
    

    -12- Constraints: T is a specialization of pair. For the second overload, is_same_v<pair<U, V>, remove_cv_t<T>> is false.

    -13- Effects: Equivalent to:

    return uses_allocator_construction_args<T>(alloc, piecewise_construct,
                                                forward_as_tuple(pr.first),
                                                forward_as_tuple(pr.second));
    
    template<class T, class Alloc>
      constexpr auto uses_allocator_construction_args(const Alloc& alloc,
                                                      remove_cv_t<T>&& pr) noexcept;
    template<class T, class Alloc, class U, class V>
      constexpr auto uses_allocator_construction_args(const Alloc& alloc,
                                                      pair<U, V>&& pr) noexcept -> see below;
    

    -14- Constraints: T is a specialization of pair. For the second overload, is_same_v<pair<U, V>, remove_cv_t<T>> is false.

    -15- Effects: Equivalent to:

    return uses_allocator_construction_args<T>(alloc, piecewise_construct,
                                                forward_as_tuple(std::move(pr).first),
                                                forward_as_tuple(std::move(pr).second));
    

[2021-03-12; Reflector poll]

Set priority to 3 following reflector poll.

Previous resolution [SUPERSEDED]:

This wording is relative to N4878.

  1. Edit 20.2.2 [memory.syn], header <memory> synopsis, as indicated:

    namespace std {
      […]
      // 20.2.8.2 [allocator.uses.construction], uses-allocator construction
      […]
    
      template<class T, class Alloc, class U, class V>
        constexpr auto uses_allocator_construction_args(const Alloc& alloc,
                                                        const pair<U, V>& pr) noexcept -> see below;
    
      template<class T, class Alloc, class U, class V>
        constexpr auto uses_allocator_construction_args(const Alloc& alloc,
                                                        pair<U, V>&& pr) noexcept -> see below;
    
    
      template<class T, class Alloc, class U>
        constexpr auto uses_allocator_construction_args(const Alloc& alloc, U&& u) noexcept;
      […]
    }
    
  2. Add the following to 20.2.8.2 [allocator.uses.construction]:

      template<class T, class Alloc, class U>
        constexpr auto uses_allocator_construction_args(const Alloc& alloc, U&& u) noexcept;
    

    -?- Let FUN be the function template:

    
      template<class A, class B>
      void FUN(const pair<A, B>&);
    

    -?- Constraints: T is a specialization of pair, and the expression FUN(u) is not well-formed when considered as an unevaluated operand.

    -?- Effects: Equivalent to:

    
    return make_tuple(pair-constructor{alloc, u});
    

    where pair-constructor is an exposition-only class defined as follows:

    
    struct pair-constructor {
      using pair-type = remove_cv_t<T>;            // exposition only
    
      constexpr operator pair-type() const {
        return do-construct(std::forward<U>(u));
      }
    
      constexpr auto do-construct(const pair-type& p) const {  // exposition only
        return make_obj_using_allocator<pair-type>(alloc, p);
      }
      constexpr auto do-construct(pair-type&& p) const {  // exposition only
        return make_obj_using_allocator<pair-type>(alloc, std::move(p));
      }
    
      const Alloc& alloc;  // exposition only
      U& u;                // exposition only
    };
    

[2021-12-02 Tim updates PR to avoid public exposition-only members]

[2022-01-31; Reflector poll]

Set status to Tentatively Ready after seven votes in favour during reflector poll.

[2022-02-10 Approved at February 2022 virtual plenary. Status changed: Tentatively Ready → WP.]

Proposed resolution:

This wording is relative to N4901.

  1. Edit 20.2.2 [memory.syn], header <memory> synopsis, as indicated:

    namespace std {
      […]
      // 20.2.8.2 [allocator.uses.construction], uses-allocator construction
      […]
      template<class T, class Alloc, class U, class V>
        constexpr auto uses_allocator_construction_args(const Alloc& alloc,
                                                        pair<U, V>& pr) noexcept;
    
      template<class T, class Alloc, class U, class V>
        constexpr auto uses_allocator_construction_args(const Alloc& alloc,
                                                        const pair<U, V>& pr) noexcept;
    
      template<class T, class Alloc, class U, class V>
        constexpr auto uses_allocator_construction_args(const Alloc& alloc,
                                                        pair<U, V>&& pr) noexcept;
    
      template<class T, class Alloc, class U, class V>
        constexpr auto uses_allocator_construction_args(const Alloc& alloc,
                                                        const pair<U, V>&& pr) noexcept;
    
      template<class T, class Alloc, class U>
        constexpr auto uses_allocator_construction_args(const Alloc& alloc, U&& u) noexcept;
      […]
    }
    
  2. Add the following to 20.2.8.2 [allocator.uses.construction]:

      template<class T, class Alloc, class U>
        constexpr auto uses_allocator_construction_args(const Alloc& alloc, U&& u) noexcept;
    

    -?- Let FUN be the function template:

    
      template<class A, class B>
      void FUN(const pair<A, B>&);
    

    -?- Constraints: T is a specialization of pair, and the expression FUN(u) is not well-formed when considered as an unevaluated operand.

    -?- Let pair-constructor be an exposition-only class defined as follows:

    
    class pair-constructor {
      using pair-type = remove_cv_t<T>;            // exposition only
    
      constexpr auto do-construct(const pair-type& p) const {  // exposition only
        return make_obj_using_allocator<pair-type>(alloc_, p);
      }
      constexpr auto do-construct(pair-type&& p) const {  // exposition only
        return make_obj_using_allocator<pair-type>(alloc_, std::move(p));
      }
    
      const Alloc& alloc_;  // exposition only
      U& u_;                // exposition only
    
    public:
      constexpr operator pair-type() const {
        return do-construct(std::forward<U>(u_));
      }
    };
    

    -?- Returns: make_tuple(pc), where pc is a pair-constructor object whose alloc_ member is initialized with alloc and whose u_ member is initialized with u.

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy