# User Defined Literals

A practical guide to user-defined literals in C++, which allow us to write more descriptive and expressive values
This lesson is part of theÂ course:

### Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Free, Unlimited Access
###### Ryan McCombe
Updated

As a general goal, we want our code to be descriptive. In the very first lesson, we discussed the importance of having descriptive identifiers - which include variables and function names. A variable called Health is more descriptive than one called x.

The idea of custom types takes that idea further. Types like Distance and Temperature are inherently more descriptive than types like int and float.

In this lesson, weâ€™ll see how we can even make values more descriptive. In the following code, itâ€™s clear weâ€™re adding a value of 3 to a variable called Distance:

Distance += 3;

But three what? Three centimeters? Three meters? ThreeÂ kilometers?

With user-defined literals, we can be moreÂ expressive:

Distance += 3_meters;
Distance += 4_kilometers;
Distance += 5_miles;

Behind the scenes, these literals are calling functions that we can define to meet our specific requirements. Let's see how we can set thisÂ up

## Examples of User-Defined Literals

Some examples of user-defined literalsÂ include:

• 3_meters
• 3.14_radians
• "192.128.0.1"_ip

They all follow the same pattern - they have 3 components, inÂ order:

• A value, e.g. 3, 3.14, or "192.128.0.1"
• An underscore, _
• A name, e.g. meters, radians, or ip

### Do we need the underscore?

We may have noticed that built-in literals do not have intermediate underscores. For example, a std::string literal looks like "Hello"s, rather than "Hello"_s.

The option to define literals without an underscore is reserved for standard library literals, including ones that may come in the future. The C++ standard requires that the suffix of user-defined literals must start with an _.

However, not all compilers are forcing this standard, and some books and learning resources are also not including the _ in user-definedÂ literals.

Removing the _ does make our code more concise - 3km looks nicer than 3_km. However, there are someÂ issues:

• A literal without a _ looks like a standard library literal. This is confusing to other developers reading or working on our code
• If a future version of the spec adds a built-in literal with the same name as ours, our code gets significantly more confusing
• Some compilers do enforce the rule, and those that currently donâ€™t may do so in the future. We generally want our code to be as portable as possible, and deviating from the standard reduces our portability

## Creating User-Defined Literals

User-defined literals are, in effect, another way to call a function that weÂ define.

The function that will be invoked by the 3_meters literal will have thisÂ syntax:

#include <iostream>

void operator""_meters(unsigned long long x){
std::cout << "Used _meters with arg: " << x;
}

int main(){
3_meters;
}
Used _meters with arg: 3

Let's break down the various components of thisÂ function.

### Function Name

The name begins with operator"", followed by an underscore, and then the name we want to use for the literal. ForÂ example:

• 3_meters will invoke a function called operator""_meters
• 3.14_radians will invoke a function called operator""_radians
• "192.168.0.1"_ip will invoke a function called operator""_ip
• 'C'_grade will invoke a function called operator""_grade

### Function Parameter Type

The value we have before the _ of the literal will be passed to the function as an argument. The only values we can support are specific types of integers, floating point numbers, characters, or strings. The typesÂ are:

• Integers - unsigned long long int
• Floats - long double
• Strings - const char*
• Characters - char

With const char* literals, we can include a second function parameter, which receives the length of theÂ string:

#include <iostream>

void operator""_ip(const char* x, size_t size) {
std::cout << "Called _ip with a string"
" of size: " << size;
}

int main() {
"192.168.0.1"_ip;
}
Called _ip with a string of size: 11

Even though the integer must be unsigned, we can still use the negation operator -. Weâ€™ll discuss this later in thisÂ lesson.

There are additional options for wide characters and wide strings. Weâ€™ll discuss wide characters and strings in the nextÂ chapter.

### Function Return Type and Body

We are free to return any type from our user-defined literal functions, including customÂ types

Similarly, we are free to implement the function body in whatever way weÂ want

## Use Case: Conversions

The most common use case for user-defined literals is to handle conversions. We already saw examples of this in the chrono literals, which gave us time-based literals like 3d, 5h, and 20min.

We can implement similar literals in our code - for example, we could implement literals to give us a descriptive syntax for weights, currencies, orÂ distances.

The following example demonstrates literals for converting distances toÂ meters:

#include <iostream>

float operator""_mm(long double D){
return D / 1000;
}

float operator""_cm(long double D){
return D / 100;
}

float operator""_in(long double D){
return D / 39.37;
}

float operator""_ft(long double D){
return D / 3.28;
}

float operator""_m(long double D){
return D;
}

float operator""_km(long double D){
return D * 1000;
}

int main(){
float Distance{3.0_m};

std::cout << "Distance: " << Distance <<
" meters";

Distance += 2.0_ft;
std::cout << "\nDistance: " << Distance <<
" meters";
}
Distance: 3 meters
Distance: 3.60976 meters

## User-Defined Literals in a Namespace

Literals are typically defined in an external file, which is globally available across our project. As part of this, itâ€™s often sensible to wrap them in a namespace, to prevent namingÂ conflicts:

namespace distance_literals{
float operator""_mm(long double D){
return D / 1000;
}

// ...
}

We can then implement a using namespace statement anywhere we need to use ourÂ literals:

int main(){
using namespace distance_literals;
float Distance{3.0_mm};
}

## Returning Custom Types

We are not restricted to returning built-in types from our literals. We can return any type we want. The following examples return a custom Distance type, which has overloaded the <<Â operator:

#include <iostream>

class Distance {
public:
Distance(float Value) : Value{Value}{}
float Value;
};

std::ostream& operator<<(
std::ostream& Stream,
Distance D
){
Stream << D.Value << " meters\n";
return Stream;
}

Distance operator""_meters(long double D){
return Distance{float(D)};
}

Distance operator""_kilometers(long double D){
return Distance{float(D * 1000)};
}

Distance operator""_miles(long double D){
return Distance{float(D * 1609)};
}

int main(){
std::cout << 4.2_meters;
std::cout << 0.4_kilometers;
std::cout << 0.1_miles;
}
4.2 meters
400 meters
160.9 meters

## Negative Numbers and Precedence

Even though the values passed to our literal functions must be positive, we can still use the -Â operator:

-0.1_miles

However, itâ€™s important to understand what is going on here. The - operator has lower precedence than the user-definedÂ literal.

That means that our literal function is called with the positive value. Then, the negation operator is applied to the value that is returned from thatÂ function:

#include <iostream>

class Distance {
public:
Distance(float Value) : Value{Value}{}

Distance operator-(){
std::cout << "Negating\n";
return Distance{-Value};
}

float Value;
};

Distance operator""_miles(long double D){
return Distance{float(D * 1609)};
}

int main(){
std::cout << (-0.1_miles).Value << " meters";
}
Negating
-160.9 meters

This has a few implications. Most notably, it means the type returned must support the unary - operator. But also, we need to be mindful of the order of operations, particularly when dealing withÂ conversions.

This order of operations still returns the correct values for distances, for example, but it would not work for temperatures. 10 degrees Celsius is 50 degrees Fahrenheit, but -10 degrees Celsius is not -50 degreesÂ Fahrenheit.

Therefore, our hypothetical temperature implementation would need a little more thought to ensure conversions are respectful of this order ofÂ operations.

## Donâ€™t Overuse User-Defined Literals

When we first learn about user-defined literals, many are tempted to overuse them. The ability to define our syntax to match our exact needs is tempting, but it can beÂ overused.

For example, we could construct a custom Player object with a user-definedÂ literal:

"Legolas"_player;

We could even allow multiple arguments in a string, and then parse them out within our function orÂ class:

"Legolas,Elf,100"_player;

But, just because we can do something, doesnâ€™t mean we should. Techniques like this donâ€™t save many keystrokes and are less clear than calling a constructor the regularÂ way:

Player{"Legolas"};
Player{"Legolas", Race::Elf, 100};

This way also provides more help from our tooling. As soon as our IDE recognizes what class weâ€™re constructing, it can jump in and assist us by telling us what arguments we need. And, if we get it wrong, the compiler will throw an error at the exact location where weâ€™re passing an invalidÂ argument.

User-defined literals are a powerful way to make our code more expressive, but in almost all programs, we should be highly selective in where we deployÂ them.

## Summary

User-defined literals enhance code expressiveness and readability, enabling us to create more intuitiveÂ APIs.

### Key Learnings

• The syntax and structure of user-defined literals, including the use of the underscore and the naming conventions enforced by the C++ standard.
• How to create user-defined literals using the operator"" syntax, and the specific argument types supported, most notably unsigned long long, long double, char, and const char*.
• The importance of using the underscore in user-defined literals to avoid conflicts, differentiating them from standard library literals.
• Like any function, user-defined literals can return values by specifying a return type in the signature and using the return statement within the body.
• The common use case of user-defined literals for unit conversions, such as distances and currencies.
• The practice of defining user-defined literals within namespaces to prevent naming conflicts and enhance code organization.
• The consideration of operator precedence, especially in the context of negative numbers, and how it impacts the behavior and implementation of user-defined literals.
• The caution against overuse of user-defined literals to maintain code readability and leverage the benefits of tooling support and compiler checks.

Next Lesson

### The Spaceship Operator and Expression Rewriting

A guide to simplifying our comparison operators using C++20 features
New: AI-Powered AssistanceAI Assistance

### Questions and HelpNeed Help?

Get instant help using our free AI assistant, powered by state-of-the-art language models.

Updated
Lesson Contents

### User Defined Literals

A practical guide to user-defined literals in C++, which allow us to write more descriptive and expressive values

This lesson is part of theÂ course:

### Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Free, Unlimited Access

###### User Defined Literals

A practical guide to user-defined literals in C++, which allow us to write more descriptive and expressive values

This lesson is part of the course:

## Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Free, unlimited access

### This course includes:

• 124 Lessons
• 550+ Code Samples
• 96% Positive Reviews
• Regularly Updated
• Help and FAQ
Next Lesson

### The Spaceship Operator and Expression Rewriting

A guide to simplifying our comparison operators using C++20 features