42

While clang compiles the following line, g++ 6.1 complains about the digit separator (see live example on Coliru):

auto time = 01'23s;

Which compiler, if any, is correct according to the C++14 standard (N3796)?

Otherwise, is allowing digit separators (§2.14.2) just an implementation detail in the user-defined literals (§2.14.8) of the <chrono> library (§20.12.5.8)? IMHO it should be not, since these literals are defined on unsigned long long parameters.

I remember Howard Hinnant using 10'000s as an example during his CppCon 2016 talk "A <chrono> tutorial" (at about 42 minutes in his talk).


(Please note, that I did not intend to code "1 minute and 23 seconds", which is only correct by accident, since the octal literal 0123 is 64 + 16 + 3 == 83. For that purpose I should write

auto time = 1min + 23s;

but that possible misleading interpretation is not part of the question.)

Columbo
  • 60,038
  • 8
  • 155
  • 203
René Richter
  • 3,839
  • 3
  • 34
  • 42
  • 1
    It appears to be a library bug and should be reported. My limited experiments suggest that `libstdc++` defines the wrong kind of literal operator, taking template non-type parametes `template` instead of `unsigned long long`. But this isn't required by the standard, and it's doing the wrong this. They have left out the `operator""s(unsigned long long)` operator, I think this is the basic underlying issue. (They do have `operator""s(long double)`, as required) – Aaron McDaid Oct 26 '16 at 12:56
  • 1
    The bug was reported here: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69905 (Somebody else posted this link, then deleted their comment, but I think it is the correct link!) – Aaron McDaid Oct 26 '16 at 14:35
  • 1
    @Aaron McDaid: I deleted that comment, when including it in my own answer (see below). – René Richter Oct 26 '16 at 14:41

3 Answers3

19

If you look at the grammar, user-defined-integer-literal can be octal-literal ud-suffix, and octal-literal is defined as either 0 or octal-literal ’opt octal-digit.

N4140 §2.14.8

user-defined-literal:

  • user-defined-integer-literal
  • [...]

user-defined-integer-literal:

  • octal-literal ud-suffix
  • [...]

N4140 §2.14.2

octal-literal:

  • 0
  • octal-literal ’opt octal-digit

So 01'23s is a perfectly valid literal.

krzaq
  • 16,240
  • 4
  • 46
  • 61
5

WLOG for decimal literals:

[lex.ext]:

user-defined-integer-literal:
    decimal-literal ud-suffix

[lex.icon]:

decimal-literal:
    nonzero-digit
    decimal-literal  opt  digit

I.e. yes, digit separators are allowed in UDLs.

Columbo
  • 60,038
  • 8
  • 155
  • 203
5

This seems to be a bug in GCC's implementation of the <chrono> library, as @Aaron McDaid suggested. There is a (currently unconfirmed) bug report: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69905

GCC's libstdc++ implements two signatures for std::chrono_literals:

constexpr chrono::duration<long double>
operator""s(long double __secs)
{ return chrono::duration<long double>{__secs}; }

template <char... _Digits>
  constexpr chrono::seconds
  operator""s()
  { return __check_overflow<chrono::seconds, _Digits...>(); }

The template version giving the error is not required by the standard. When adding

constexpr chrono::seconds
operator""s(unsigned long long __secs)
{ return chrono::seconds{__secs}; }

to the <chrono> header (of my local installation) the error dissappears.

However, GCC's library implementers may have left out this version on purpose, so they can prevent an unwanted unsigned to signed conversion, since seconds are defined as

typedef duration<int64_t> seconds;

Edit:

As recently pointed out by Jonathan Wakely in the comments of the bug report, the implementation was chosen by design in connection with an open Library Working Group issue, but did not take digit separators into account.

René Richter
  • 3,839
  • 3
  • 34
  • 42
  • The issue is that the standard requires compile-time overflow checking, but using a cooked literal operator doesn't allow you to do that. – T.C. Oct 26 '16 at 14:42
  • I don't understand what a "cooked" operator is, @T.C.. Do you mean that the "straightforward" `unsigned long long` overload isn't able to do that checking at compile time? And therefore the template is required? – Aaron McDaid Oct 26 '16 at 14:52
  • @AaronMcDaid Correct. "cooked" = one taking an `unsigned long long`; "raw" = one that takes characters. – T.C. Oct 26 '16 at 14:59