3294. zoned_time deduction guides misinterprets string/char*

Section: 30.11.7.1 [time.zone.zonedtime.overview] Status: C++20 Submitter: Tomasz Kamiński Opened: 2019-09-14 Last modified: 2021-02-25

Priority: 0

View all other issues in [time.zone.zonedtime.overview].

View all issues with C++20 status.

Discussion:

The current deduction guide for zoned_time for the following declarations

zoned_time zpc("America/New_York", std::chrono::system_clock::now());
zoned_time zps(std::string("America/New_York"), std::chrono::system_clock::now());

will attempt to produce a zoned_time instance with const char* (for zpc) and with std::string (for zps), respectively, as the deduced type for the TimeZonePtr template parameter. This is caused by the fact that the unconstrained TimeZonePtr deduction guide template will produce better candidates and will be selected by overload resolution.

The proposed resolution merges the deduction of the std::string_view/TimeZonePtr deduction guides into one guide, that deduces const time_zone* for any type convertible to string_view. This is necessary to override the deduction from TimeZonePtr constructor candidates.

In addition, we disable the deduction from string_view constructors, that would produce better candidates than the deduction guides and create zoned_time instances with durations coarser than seconds (causing similar issue as LWG 3232):

std::chrono::local_time<hours> lh(10h);
std::chrono::zoned_time zt1("Europe/Helsinki", lh);
std::chrono::zoned_time zt2(std::string("Europe/Helsinki"), lh);
std::chrono::zoned_time zt3(std::string_view("Europe/Helsinki"), lh);

Without disabling the deduction from the string_view constructor, the type of the zt3 variable would be deduced to zoned_time<hours>, with the proposed change the types of the variables zt1, zt2, and zt3 are consistently deduced as zoned_time<seconds>.

Finally, the wording eliminates the unnecessary zoned_time<Duration> guide (covered by zoned_time<Duration, TimeZonePtr2>).

The change was implemented in the example implementation. The dedicated test can be found here.

[2019-10-31 Issue Prioritization]

Status to Tentatively Ready and priority to 0 after five positive votes on the reflector.

Proposed resolution:

This wording is relative to N4830.

  1. Modify 30.11.7.1 [time.zone.zonedtime.overview], class template zoned_time synopsis, as indicated:

    namespace std::chrono {
      […]
      zoned_time() -> zoned_time<seconds>;
      
      template<class Duration>
        zoned_time(sys_time<Duration>)
          -> zoned_time<common_type_t<Duration, seconds>>;
          
      template<class TimeZonePtrOrName>
        using time-zone-representation =
          conditional_t<is_convertible_v<TimeZonePtrOrName, string_view>, 
            const time_zone*,
            remove_cv_ref<TimeZonePtrOrName>>; // exposition only
    
      template<class TimeZonePtrOrName>
        zoned_time(TimeZonePtrOrName&&)
          -> zoned_time<seconds, time-zone-representation<TimeZonePtr>>;
      
      template<class TimeZonePtrOrName, class Duration>
        zoned_time(TimeZonePtrOrName&&, sys_time<Duration>)
          -> zoned_time<common_type_t<Duration, seconds>, 
            time-zone-representation<TimeZonePtrOrName>>;
      
      template<class TimeZonePtrOrName, class Duration>
        zoned_time(TimeZonePtrOrName&&, local_time<Duration>, choose = choose::earliest)
          -> zoned_time<common_type_t<Duration, seconds>, 
            time-zone-representation<TimeZonePtrOrName>>;
      
      template<class TimeZonePtr, class Duration>
        zoned_time(TimeZonePtr, zoned_time<Duration>, choose = choose::earliest)
          ->> zoned_time<common_type_t<Duration, seconds>, TimeZonePtr>;
      
      zoned_time(string_view) -> zoned_time<seconds>;
    
      template<class Duration>
        zoned_time(string_view, sys_time<Duration>)
          -> zoned_time<common_type_t<Duration, seconds>>;
      
      template<class Duration>
        zoned_time(string_view, local_time<Duration>, choose = choose::earliest)
          -> zoned_time<common_type_t<Duration, seconds>>;
      
      template<class Duration, class TimeZonePtrOrName, class TimeZonePtr2>
        zoned_time(TimeZonePtrOrName&&, zoned_time<Duration, TimeZonePtr2>, choose = choose::earliest)
         -> zoned_time<Duration, time-zone-representation<TimeZonePtrOrName>>;
    }
    

    -1- zoned_time represents a logical pairing of a time_zone and a time_point with precision Duration. zoned_time<Duration> maintains the invariant that it always refers to a valid time zone and represents a point in time that exists and is not ambiguous in that time zone.

    -2- If Duration is not a specialization of chrono::duration, the program is ill-formed.

    -?- Every constructor of zoned_time that accepts a string_view as first parameter does not participate in class template argument deduction (12.2.2.9 [over.match.class.deduct]).