Modern C++ Features – User-Defined Literals

Contents

User-defined literals are a convenient feature added in C++11.

C++ always had a number of built-in ways to write literals: Pieces of source code that have a specific type and value. They are part of the basic building blocks of the language:

32 043 0x34   //integer literals, type int
4.27 5E1      //floating point literals, type double
'f', '\n'     //character literals, type char
"foo"         //string literal, type const char[4]
true, false   //boolean literals, type bool

These are only the most common ones, there are many more, including some newcomers in the newer standards. Other literals are nullptr and different kinds of prefixes for character and string literals. There also are suffixes we can use to change the type of a built-in numeric literal:

32u     //unsigned int
043l    //long
0x34ull //unsigned long long
4.27f   //float
5E1l    //long double

Suffixes for user-defined literals

With C++11 we got the possibility to define our own suffixes. They can be applied to integer, floating point, character and string literals of any flavor. The suffixes must be valid identifiers and start with an underscore – those without underscore are reserved for future standards.

Syntax

Using the literals

User-defined literals are basically normal function calls with a fancy syntax. I’ll show you in a second how those functions are defined. First, let’s see some examples of how they are used:

//user-defined integer literal with suffix _km
45_km                      
//user-defined floating point literal with suffix _mi
17.8e2_mi                  
//user-defined character literal with suffix _c
'g'_c                      
//user-defined character literal (char32_t) with suffix _c
U'%'_c                     
//user-defined string literal with suffix _score
"under"_score              
//user-defined string literal (raw, UTF8) with suffix _stuff
u8R"##("(weird)")##"_stuff 

Defining literal operators

The functions are called literal operators. Given an appropriate class for lengths, the definition of literal operators that match the first two examples above could look like this:

Length operator "" _km(unsigned long long n) {
  return Length{n, Length::KILOMETERS};
}

Length operator ""_mi(long double d) {
  return Length{d, Length::MILES};
}

More general, the syntax for the function header is <ReturnType> operator "" <Suffix> (<Parameters>). The return type can be anything, including void. As you see, there can be whitespace between the "" and the suffix – unless the suffix standing alone would be a reserved identifier or keyword. That means, if we want our suffix to start with a capital letter after the underscore, e.g. ‘_KM’, there may be no white space. (Identifier with underscores followed by capitals are reserved for the standard implementation.)

The allowed parameter lists are constrained: For a user-defined integral or floating point literal, you can already see an example above. The compiler at first looks for an operator that takes an unsigned long long or long double, respectively. If such an operator can not be found, there has to be either one taking a char const* or a template<char...> operator taking no parameters.

In the case of the so-called raw literal operator taking a const char, the character sequence constituting the integral or floating point literal is passed as the parameter. In the case of the template, it is passed as the list of template arguments. E.g. for the _mi example above this would instantiate and call operator ""_mi<'1', '7', '.', '8', 'e', '2'>().

Use cases

The example with the units above is a pretty common one. You will have noted that both operators return a Length. The class would have an internal conversion for the different units, so with the user defined literals it would be easy to mix the units without crashing your spaceship:

auto length = 32_mi + 45.4_km;
std::cout << "It's " << length.miles() << " miles\n"; //60.21
std::cout << "or " << length.kilometers() << " kilometers\n";    //96.899

The standard library also contains a bunch of these (and yes, they still are called “user-defined” in standard speak). They are not directly in namespace std but in subnamespaces of std::literals:

  • The suffixes i, if and il from std::literals::complex_literals are for the imaginary part of std::complex numbers. So, 3.5if is the same as std::complex<float>{0, 3.5f}
  • The suffixes h, min, s, ms, us and ns from std::literals::chrono_literals create durations in std::chrono for hours, minutes, seconds, milli-, micro- and nanoseconds, respectively.
  • In std::literals::string_literals we have the suffix s to finally create a std::string right from a string literal instead of tossing around char const*.

A word of caution

While user defined literals look very neat, they are not much more than syntactic sugar. There is not much difference between defining and calling a literal operator with "foo"_bar and doing the same with an ordinary function as bar("foo"). In theory, we could write literal operators that have side effects and do anything we want, like a normal function.

However, that is not what people would expect from something that does not look like “it does something”. Therefore it is best to use user defined literals only as obvious shorthand for the construction of values.

Playing with other modern C++ features

A while ago I came across a case where I had to loop over a fixed list of std::strings defined at compile time. In the old days before C++11, the code would have looked like this:

static std::string const strings[] =  {"foo", "bar", "baz"};

for (std::string const* pstr = strings; pstr != strings+3; ++pstr) {
  process(*pstr);
}

This is horrible. Dereferencing the pointer and the hard-coded 3 in the loop condition just seem not right. I could have used an std::vector<std::string> here, but that would mean a separate function to prefill and initialize the const vector since there were no lambdas.

Today we have range based for, initializer_list, auto and user-defined literals for strings:

using namespace std::literals::string_literals;

//...

for (auto const& str : {"foo"s, "bar"s, "baz"s}) {
  process(str);
}

And the code looks just as simple as it should.

Previous Post
Next Post

14 Comments


  1. Do you have the code impl. for Length?

    Reply

    1. Hi Carl, thanks for the question. Currently, I don’t have an implementation at hand. It should be straightforward though, so I’d consider it an “exercise for the reader”. Feel free to contact me if you have problems trying to implement it.

      Reply



  2. Note, the string literals are only available since C++14. Also in your example you can just as easily avoid them:

    for (auto const& str : {“foo”, “bar”, “baz”}) {
    process(str);
    }

    Reply

    1. Hi Ondra, thanks for the hint. Yes, string_literals have been added in C++14 – I usually do not distinguish too much between 11, 14 and 17. I just recommend that to play with modern C++ features, one should strive to use the latest compiler available. There are compilers from 2012 that support C++14 features, and there are compilers from 2015 that still lack C++11 features, so in general, it does not matter too much which standard version a feature has been introduced. It’s more dependent on the compiler or its standard library supporting a specific feature.

      Avoiding the string_literals like you suggest would change the type of str from std::string to char const* which may or may not yield a different result.

      Reply

  3. Interesting. I am wondering if and how I could use the std user defined literals without a “using namespace …”. What would that syntax look like?

    Reply

    1. auto s = std::literals::string_literals::operator””s(“foo”, 4);

      is what happens behind the scenes when you write

      “foo”s

      Reply

  4. Modern C++ accept for a few new things like smart pointers and auto really is a dogs dinner of a ‘new’ language!

    Really need C++ 18 that removes a lot of these pointless new features and make the language less complicated and easier to use.

    Sorry but those on the C++ guiding committee might be clever but they are not smart!

    Reply

    1. I guess there are people on the committee that have insights why those features might not be so pointless as you perceive them.

      Reply

  5. Unfortunately, you have your spaceship crashed 🙂
    32_mi + 45.4_km is nowhere near the values presented in the code. It’s 60.22 miles or 96.89 km accordingly. Apart from that, very good article!

    Reply

    1. Thanks – I obviously forgot the multiplication with the MILE constant in my test code…

      Reply

  6. Nice article, but the values for the mi / km example are wrong; it looks like you just added both values as if they were km. The sums should be 96.9 km or 60.21 mi.

    Reply

  7. Instead of using namespace std::literals::string_literals;, you can simply use using namespace std::string_literals; since both literals and string_literals are inline namespaces. They are explicitly designed to be used as such 🙂

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *