3540. §[format.arg] There should be no const in basic_format_arg(const T* p)

Section: 28.5.8.1 [format.arg] Status: C++23 Submitter: S. B. Tam Opened: 2021-04-07 Last modified: 2023-11-22

Priority: Not Prioritized

View all other issues in [format.arg].

View all issues with C++23 status.

Discussion:

When P0645R10 "Text formatting" was merged into the draft standard, there was a typo: an exposition-only constructor of basic_format_arg is declared as accepting const T* in the class synopsis, but is later declared to accept T*. This was editorial issue 3461 and was resolved by adding const to the redeclaration.

As it is, constructing basic_format_arg from void* will select template<class T> explicit basic_format_arg(const T& v) and store a basic_format_arg::handle instead of select template<class T> basic_format_arg(const T*) and store a const void*, because void*void*const& is identity conversion, while void*const void* is qualification conversion.

While this technically works, it seems that storing a const void* would be more intuitive.

Hence, I think const should be removed from both declarations of basic_format_arg(const T*), so that construction from void* will select this constructor, resulting in more intuitive behavior.

[2021-04-20; Reflector poll]

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

[2021-06-07 Approved at June 2021 virtual plenary. Status changed: Voting → WP.]

Proposed resolution:

This wording is relative to N4885.

  1. Modify 28.5.8.1 [format.arg] as indicated:

    namespace std {
      template<class Context>
      class basic_format_arg {
      public:
        class handle;
    
      private:
        using char_type = typename Context::char_type;                     // exposition only
    
        variant<monostate, bool, char_type,
          int, unsigned int, long long int, unsigned long long int,
          float, double, long double,
          const char_type*, basic_string_view<char_type>,
          const void*, handle> value;                                      // exposition only
    
        template<class T> explicit basic_format_arg(const T& v) noexcept;  // exposition only
        explicit basic_format_arg(float n) noexcept;                       // exposition only
        explicit basic_format_arg(double n) noexcept;                      // exposition only
        explicit basic_format_arg(long double n) noexcept;                 // exposition only
        explicit basic_format_arg(const char_type* s);                     // exposition only
    
        template<class traits>
          explicit basic_format_arg(
            basic_string_view<char_type, traits> s) noexcept;              // exposition only
    
        template<class traits, class Allocator>
          explicit basic_format_arg(
            const basic_string<char_type, traits, Allocator>& s) noexcept; // exposition only
        
        explicit basic_format_arg(nullptr_t) noexcept;                     // exposition only
    
        template<class T>
          explicit basic_format_arg(const T* p) noexcept;                  // exposition only
      public:
        basic_format_arg() noexcept;
    
        explicit operator bool() const noexcept;
      };
    }
    

    […]

    template<class T> explicit basic_format_arg(const T* p) noexcept;
    

    -12- Constraints: is_void_v<T> is true.

    -13- Effects: Initializes value with p.

    -14- [Note 1: Constructing basic_format_arg from a pointer to a member is ill-formed unless the user provides an enabled specialization of formatter for that pointer to member type. — end note]