2278. User-defined literals for Standard Library types

Section: 30.2 [time.syn], 27.4 [string.classes] Status: C++14 Submitter: Howard Hinnant Opened: 2013-07-22 Last modified: 2017-09-07

Priority: Not Prioritized

View all other issues in [time.syn].

View all issues with C++14 status.

Discussion:

This paper adds user-defined literals for string, complex and chrono types. It puts each new literal signature in an inline namespace inside of std. Section 3.1 of the paper gives the rationale for doing this:

As a common schema this paper proposes to put all suffixes for user defined literals in separate inline namespaces that are below the inline namespace std::literals. [Note: This allows a user either to do a using namespace std::literals; to import all literal operators from the standard available through header file inclusion, or to use using namespace std::string_literals; to just obtain the literals operators for a specific type. — end note]

This isn't how inline namespaces work.

9.8.2 [namespace.def]/p8 says in part:

Members of an inline namespace can be used in most respects as though they were members of the enclosing namespace. Specifically, the inline namespace and its enclosing namespace are both added to the set of associated namespaces used in argument-dependent lookup (3.4.2) whenever one of them is, and a using- directive (7.3.4) that names the inline namespace is implicitly inserted into the enclosing namespace as for an unnamed namespace (7.3.1.1). […]

I.e. these literals will appear to the client to already be imported into namespace std. The rationale in the paper appears to indicate that this is not the intended behavior, and that instead the intended behavior is to require the user to say:

using namespace std::literals;

or:

using namespace std::literals::string_literals;

prior to use. To get this behavior non-inlined (normal) namespaces must be used.

Originally proposed resolution:

Strike the use of "inline" from each use associated with literals, string_literals, chrono_literals.

My opinion is that this must be done prior to publishing C++14, otherwise we are stuck with this (apparently unwanted) decision forever.

Marshall Clow:

The rationale that I recall was that:

  1. Users could write "using namespace std::literals;" to get all the literal suffixes, or

  2. Users could write "using namespace std::literals::string_literals;" or "using namespace std::literals::chrono_literals;" to get a subset of the suffixes.

To accomplish that, I believe that:

  1. Namespace "std::literals" should not be inline

  2. Namespaces "std::literals::string_literals" and "std::literals::chrono_literals" should be inline

Further details see also reflector message c++std-lib-34256.

Previous resolution from Marshall Clow:

  1. Modify header <chrono> synopsis, 30.2 [time.syn], as indicated:

    namespace std {
    namespace chrono {
    […]
    } // namespace chrono
    inline namespace literals {
    inline namespace chrono_literals {
    […]
    } // namespace chrono_literals
    } // namespace literals
    } // namespace std
    
  2. Modify header <string> synopsis, 27.4 [string.classes] p1, as indicated:

    #include <initializer_list>
    
    namespace std {
    […]
    inline namespace literals {
    inline namespace string_literals {
    […]
    }
    }
    }
    

[2013-09 Chicago]

After a discussion about intent, the conclusion was that if you hoist a type with a "using" directive, then you should also get the associated literal suffixes with the type.

This is accomplished by marking namespace std::literals as inline, but for types in their own namespace inside std, then they will need to do this as well. The only case in the current library is chrono.

Marshall Clow provides alternative wording.

[2013-09 Chicago (late night issues)]

Moved to Ready, after confirming wording reflects the intent of the earlier discussion.

Proposed resolution:

This wording is relative to N3691.

  1. Modify header <chrono> synopsis, 30.2 [time.syn], as indicated:

    namespace std {
    […]
    inline namespace literals {
    inline namespace chrono_literals {
    […]
    constexpr chrono::duration<unspecified , nano> operator "" ns(long double);
    
    } // namespace chrono_literals
    } // namespace literals
    
    
    namespace chrono {
        using namespace literals::chrono_literals;
    } // namespace chrono
    
    } // namespace std