Namespaces for UDLs
Vittorio Romeo has posted a very detailed trip report from the just-concluded WG21 meeting in Jacksonville, Florida. I’m just going to comment on one very tiny piece of it:
[…] we discussed P0921R0 “Standard Library Compatibility Promises” which, similarly to the previous paper, proposed a set of guidelines that the evolution working groups should follow when debating on potential breaking changes to the Standard Library. The general ideas were agreed upon by everyone, but the second half of the paper was quite controversial. Some of the guidelines for the users, such as
Do not add
using namespace std
(or anyusing
directive for sub-namespaces ofstd
) to your code.were overly restrictive. The above, as an example, would prevent UDLs from being usable.
Preliminary reassurance: Notice that if you want to use deeply nested std
types, it
would still be totally fine — and in fact, recommended! — to use the little-known
namespace alias syntax for that purpose:
namespace fs = std::filesystem;
fs::path p("/hello/world.txt");
Okay, so what about UDLs (user-defined literals)? The problem with UDLs is that they are indicated by a single identifier-ish suffix on the end of an integer or string literal:
auto hello = "hello"s; // defines a variable of type std::string
auto timeout = 1s; // defines a variable of type std::chrono::seconds
And yes, I deliberately picked the craziest example in the standard library to demonstrate with.
The traditional way to pull these suffixes into scope is with a using
-directive:
using namespace std::literals::string_literals;
auto hello = "hello"s; // defines a variable of type std::string
auto timeout = 1s; // does not compile
But if these are global (inline
) variables, or otherwise at global scope in a header file,
then there is simply no way to get at them without a using
-directive. We’re reduced to
complicated workarounds such as
namespace detail {
using namespace std::literals;
inline constexpr auto initializer_for_timeout() { return 1s; }
};
inline auto timeout = initializer_for_timeout();
Or, of course, we could write
inline std::seconds timeout(1);
but we’re pretending that we have a plausible use-case for UDLs in the first place. :)
Namespace all the things
The obvious thing to do here would be to change the core language to permit namespace-qualified UDL suffixes. There are two reasonable syntax options for this:
inline auto timeout = std::1s; // A
inline auto timeout = 1std::s; // B
Personally, I’m partial to syntax A
, even though it looks pretty funny at first.
I initially thought that B
would be knocked out immediately, because it would conflict
with existing syntax such as
auto negative_one = "hello"s::npos;
But it turns out that that’s a syntax error in today’s C++! The only acceptable way to get at
the static members of an object’s type is via .
, not via ::
. So:
auto negative_one = "hello"s.npos; // OK
Of course C++ is nothing if not inconsistent:
auto one = 1s.count(); // Error: invalid suffix 's.count' on integer literal
(This happens because any pp-token starting with a digit extends all the way to the next
non-period-plus-or-minus punctuation character: 0x1
, 1.e10
, and 1s.count
are all
equally treated as pp-numbers by the compiler.)
So anyway. Should someone write a WG21 proposal for namespaced UDL suffixes, so that we
can remove that rationale for hanging onto using namespace ...
directives in C++ code?