24

I'm learning about user-defined literals, and confused with the following test code:

std::chrono::seconds operator"" _s(unsigned long long s) {
    return std::chrono::seconds(s);
}

std::string operator"" _str(const char *s, std::size_t len) {
    return std::string(s, len);
}

int main() {
    auto str = "xxxxx"_str;
    std::cout << str.size() << std::endl;    // works

    auto sec = 4_s;
    std::cout << sec.count() << std::endl;   // works

    std::cout << "xxxxx"_str.size() << std::endl;   // works

    std::cout << 4_s.count() << std::endl;   // does **NOT** work!

    return 0;
}

The compiler gives the following error message:

error: no matching literal operator for call to 'operator""_s.count' with argument of type 'unsigned long long' or 'const char *', and no matching literal operator template
cout << 4_s.count() << endl;

It seems that it takes _s.count as a user-defined literal. Also, a floating-point literal behaves like an integer literal.

Why do user-defined integer literals and string literals have different behavior?

Boann
  • 48,794
  • 16
  • 117
  • 146
for_stack
  • 21,012
  • 4
  • 35
  • 48
  • what about (4_s).count? – Zefick Nov 24 '17 at 06:18
  • `(4_s).count` works. You CANNOT have `(` or `)` in the `ud-suffix`. – for_stack Nov 24 '17 at 06:25
  • @for_stack It seems weird, `.` could not be used in `ud-suffix` either; can't get why `_s.count` is parsed as the whole `ud-suffix`. – songyuanyao Nov 24 '17 at 06:34
  • @songyuanyao Good to see you. It's not weird if an explanation can be found. – iBug Nov 24 '17 at 07:03
  • 3
    `4_s.count` is parsed as a [preprocessing number](http://eel.is/c++draft/lex.ppnumber#nt:pp-number) token, which is tolerated by the preprocessor and is [converted to a token](http://eel.is/c++draft/lex.phases#1.7.sentence-2) after preprocessing but before syntactic and semantic analysis, but it can't be converted to a valid token (e.g. interger literal or floating literal), and thus violates [\[lex.pptoken\]/1](http://eel.is/c++draft/lex.pptoken#1). Admittedly the compiler error is totally unhelpful in this case. – cpplearner Nov 24 '17 at 14:19

1 Answers1

23

That's how floating point literals work!!

Add a pair of parentheses and it should work:

std::cout << (4_s).count();

Or alternatively, separate them (to stop the compiler from interpreting it as an ill-formed fractional constant floating point literal):

std::cout << 4_s .count();
//              ^ Space here!

Reference: CppReference.com

In the Notes section of the reference above,

Due to maximal munch, user-defined integer and floating point literals ending in [p, P, (since C++17)] e and E, when followed by the operators + or -, must be separated from the operator with whitespace in the source:

long double operator""_E(long double);
long double operator""_a(long double);
int operator""_p(unsigned long long);

auto x = 1.0_E+2.0;  // error
auto y = 1.0_a+2.0;  // OK
auto z = 1.0_E +2.0; // OK
auto w = 1_p+2;      // error
auto u = 1_p +2;     // OK

So when it comes to dot, which is used as decimal point, it must be separated from anything behind, or it'll be treated as part of the floating point number.

I have tested the example above from CppReference and got a very similar error message:

test.cpp:19:10: error: unable to find numeric literal
operator 'operator""_E+2.0'
                    ^^^^^^
 auto x = 1.0_E+2.0;  // error

Got the point how _E+2.0 is considered as a whole ud-suffix?


My original explanation attempt can be found in the revision history of this post.

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
iBug
  • 35,554
  • 7
  • 89
  • 134
  • 3
    This question isn't about *how* to make it work; it's about *why* it doesn't work. Apparently, there's a difference in precedence when using strings versus integers, but I believe that a good answer to the question should explain why the language is designed that way. – HTNW Nov 24 '17 at 06:28
  • You beat me in adding the link to the `CppReference` page. I was reading it before you posted & updated your answer. – Francis Cugler Nov 24 '17 at 06:39
  • 1
    @FrancisCugler I added it even ***after*** I had read it all :) – iBug Nov 24 '17 at 06:41
  • I've learned over time that `CppReference` is truly an excellent resource & repository that acts as a great lookup table or guide. Trying to get any helpful information from MS well that's a whole different story... – Francis Cugler Nov 24 '17 at 06:45
  • All compilers shows the same error btw. So it is probably not a bug. – Zefick Nov 24 '17 at 06:49
  • 1
    @Zefick What is ***all***? – iBug Nov 24 '17 at 06:50
  • @iBug VC++, GCC, clang. And funny thing here is that you cannot define `operator""_s.count` even if you want. – Zefick Nov 24 '17 at 06:56