Literals are constants of built-in types (numerical, Boolean, character, character string, and pointer) that cannot be altered in a program. The language defines a series of prefixes and suffixes to specify literals (and the prefix/suffix is actually part of the literal). C++11 allows us to create user-defined literals by defining functions called literal operators, which introduce suffixes for specifying literals. These work only with numerical character and character string types.
This opens the possibility of defining both standard literals in future versions and allows developers to create their own literals. In this recipe, we will learn how to create our own cooked literals.
Getting ready
User-defined literals can have two forms: raw and cooked. Raw literals are not processed by the compiler, whereas cooked literals are values processed by the compiler (examples can include handling escape sequences in a character string or identifying numerical values such as integer 2898 from literal 0xBAD). Raw literals are only available for integral and floating-point types, whereas cooked literals are also available for character and character string literals.
How to do it...
To create cooked user-defined literals, you should follow these steps:
Define your literals in a separate namespace to avoid name clashes.
Always prefix the user-defined suffix with an underscore (_).
Define a literal operator of one of the following forms for cooked literals:
When the compiler encounters a user-defined literal with a user-defined suffix, S (it always has a leading underscore for third-party suffixes, as suffixes without a leading underscore are reserved for the standard library), it does an unqualified name lookup in order to identify a function with the name operator "" S. If it finds one, then it calls it according to the type of the literal and the type of the literal operator. Otherwise, the compiler will yield an error.
In the example shown in the How to do it... section, the literal operator is called operator "" _KB and has an argument of type unsigned long long int. This is the only integral type possible for literal operators for handling integral types. Similarly, for floating-point user-defined literals, the parameter type must be long double since for numeric types, the literal operators must be able to handle the largest possible values. This literal operator returns a constexpr value so that it can be used where compile-time values are expected, such as specifying the size of an array, as shown in the preceding example.
When the compiler identifies a user-defined literal and has to call the appropriate user-defined literal operator, it will pick the overload from the overload set according to the following rules:
For integral literals: It calls in the following order: the operator that takes an unsigned long long, the raw literal operator that takes a const char*, or the literal operator template.
For floating-point literals: It calls in the following order: the operator that takes a long double, the raw literal operator that takes a const char*, or the literal operator template.
For character literals: It calls the appropriate operator, depending on the character type (char, wchar_t, char16_t, and char32_t).
For string literals: It calls the appropriate operator, depending on the string type, that takes a pointer to the string of characters and the size.
In the following example, we're defining a system of units and quantities. We want to operate with kilograms, pieces, liters, and other types of units. This could be useful in a system that can process orders and you need to specify the amount and unit for each article.
The following are defined in the namespace units:
A scoped enumeration for the possible types of units (kilogram, meter, liter, and pieces):
enumclassunit { kilogram, liter, meter, piece, };
A class template to specify quantities of a particular unit (such as 3.5 kilograms or 42 pieces):
Literal operators to create quantity literals, defined in an inner namespace called unit_literals. The purpose of this is to avoid possible name clashes with literals from other namespaces.
If such collisions do happen, developers could select the ones that they should use using the appropriate namespace in the scope where the literals need to be defined:
By looking carefully, you can note that the literal operators defined earlier are not the same:
_kg is defined for both integral and floating-point literals; that enables us to create both integral and floating-point values such as 1_kg and 1.0_kg.
_l and _m are defined only for floating-point literals; this means we can only define quantity literals for these units with floating points, such as 4.5_l and 10.0_m.
_pcs is only defined for integral literals; this means we can only define quantities of an integer number of pieces, such as 42_pcs.
Having these literal operators available, we can operate with various quantities. The following examples show both valid and invalid operations:
usingnamespace units;
usingnamespace unit_literals;
auto q1{ 1_kg }; // OKauto q2{ 4.5_kg }; // OKauto q3{ q1 + q2 }; // OKauto q4{ q2 - q1 }; // OK// error, cannot add meters and piecesauto q5{ 1.0_m + 1_pcs };
// error, cannot have an integer number of litersauto q6{ 1_l };
// error, can only have an integer number of piecesauto q7{ 2.0_pcs}
q1 is a quantity of 1 kg; this is an integer value. Since an overloaded operator "" _kg(unsigned long long const) exists, the literal can be correctly created from the integer 1. Similarly, q2 is a quantity of 4.5 kilograms; this is a real value. Since an overloaded operator "" _kg(long double) exists, the literal can be created from the double floating-point value 4.5.
On the other hand, q6 is a quantity of 1 liter. Since there is no overloaded operator "" _l(unsigned long long), the literal cannot be created. It would require an overload that takes an unsigned long long, but such an overload does not exist. Similarly, q7 is a quantity of 2.0 pieces, but piece literals can only be created from integer values and, therefore, this generates another compiler error.
There's more...
Though user-defined literals are available from C++11, standard literal operators have been available only from C++14. Further standard user-defined literals have been added to the next versions of the standard. The following is a list of these standard literal operators:
operator""s for defining std::basic_string literals and operator""sv (in C++17) for defining std::basic_string_view literals:
operator""y for creating an std::chrono::year literal and operator""d for creating an std::chrono::day literal that represents a day of a month, both added to C++20:
usingnamespacestd::chrono_literals;
auto year { 2020y }; // std::chrono::yearauto day { 15d }; // std::chrono::day
operator""if, operator""i, and operator""il for creating an std::complex value:
usingnamespacestd::complex_literals;
auto c{ 12.0 + 4.5i }; // std::complex<double>
The standard user-defined literals are available in multiple namespaces. For instance, the ""s and ""sv literals for strings are defined in the namespace std::literals::string_literals.
However, both literals and string_literals are inlined namespaces. Therefore, you can access the literals with using namespace std::literals, using namespace std::string_literals, or using namespace std::literals::string_literals. In the previous examples, the second form was preferred.
See also
Using raw string literals to avoid escaping characters to learn how to define string literals without the need to escape special characters
Creating raw user-defined literals to understand how to provide a custom interpretation of an input sequence so that it changes the normal behavior of the compiler
Using inline namespaces for symbol versioning in Chapter 1, Learning Modern Core Language Features, to learn how to version your source code using inline namespaces and conditional compilation