The UDLs Included in the C++ Standard Library - dummies

The UDLs Included in the C++ Standard Library

By John Paul Mueller, Jeff Cogswell

Even though you can currently create User-Defined Literals (UDLs) for some basic types, there are many situations where developers need UDLs for classes as well. In some cases, these classes are part of the Standard Library. There are now consistent and standardized UDLs attached to some classes. Following are some of the more important classes and how to use them.

std::basic_string

The std::basic_string class makes it possible to work with sequences of char-like objects. The class currently has templates defined for

  • char

  • wchar_t

  • char16_t

  • char32_t

However, the class could easily be extended for other kinds of characters. In addition, the templates make it possible to specify character traits and the method used to store the data in memory. The essential idea behind the basic_string is to make it possible to accommodate a variety of character types within one character class to simplify coding.

In C++ 14, the Standard Library includes built-in literal support for basic_string. All you need to do is add the s suffix to a string to create one. However, it’s important to get an idea of how all this works behind the scenes. The BasicString example demonstrates three techniques for creating a basic_string object.

#include <iostream>
#include <typeinfo>
using namespace std;
string operator" _s(const char * str, unsigned len)
{
  return string{str, len};
}
int main()
{
    basic_string<char> StdString = "This is a standard string.";
    auto AutoString = "This is an auto string."_s;
    // Remove comment when your compiler supports C++ 14.
    //auto UDLString = "This is a UDL string."s;
    cout << StdString << endl << typeid(StdString).name() << endl;
    cout << AutoString << endl << typeid(AutoString).name() << endl;
    // Remove comment when your compiler supports C++ 14.
    //cout << UDLString << endl << typeid(UDLString).name() << endl;
    return 0;
}

This example performs three essential levels of conversion so that you can see the progression from one to another. In the first case, you see the straightforward method for creating a simple basic_string object, StdString.

As you can see, it works just like any other template. The second case relies on a C++ 11 type operator definition to emulate the UDL that is included as part of C++ 14. All you really need to know for now is that the operator makes it possible to use a shortcut when creating basic_string objects.

The third case shows the C++14 version of the same _s definition, but this one is built right into the Standard Library so you don’t have to do anything special to use it. In all three cases, you create the same basic_string object type, but the technique differs each time. When you run this example, you see the following output:

This is a standard string.
Ss
This is an auto string.
Ss
This is a UDL string.
Ss

This output shows the mangled form of the name() function output. The fact that all three strings are the same tells you they’re the same object type. If you want to see the unmangled names, you can use the Demangle() function provided.

std::complex

You may or may not remember complex numbers from school. A complex number consists of a real number and an imaginary number that are paired together. Real-world uses for complex numbers include:

  • Electrical engineering

  • Fluid dynamics

  • Quantum mechanics

  • Computer graphics

  • Dynamic systems

There are other uses for complex numbers, too, but this list should give you some ideas. In general, if you aren’t involved in any of these disciplines, you probably won’t ever encounter complex numbers. However, the Standard Library provides full support for complex numbers, just in case you do need them.

As with the BasicString example, this example shows the progression from a standard declaration to the C++ 14 suffix. The ComplexNumber example demonstrates all three stages so you can see how both the C++ 14 suffix and the C++ 11 UDL forms work.

#include <iostream>
#include <complex>
using namespace std;
complex<long double> operator" _i(long double Value)
{
    return complex<double>(0, Value);
}
int main()
{
    complex<double> StdComplex(0, 3.14);
    auto AutoComplex = 3.14_i;
    // Remove comment when your compiler supports C++ 14.
    //auto UDLComplex = 3.14i;
    cout << StdComplex.real() << " " << StdComplex.imag() << endl;
    cout << AutoComplex.real() << " " << AutoComplex.imag() << endl;
    // Remove comment when your compiler supports C++ 14.
    //cout << UDLComplex.real() << " " << UDLComplex.imag() << endl;
    return 0;
}

The example declares variables of all three types and assigns values to them. It then displays both the real and imaginary parts of the number. When you run this example, you see the following output:

0 3.14
0 3.14
0 3.14

You can create three kinds of complex numbers. The following list shows the suffixes used for each type:

  • i: double

  • if: float

  • il: long double

std::chrono::duration

The chrono::duration class serves to mark the passage of time. It answers the question of how much time has elapsed between two events. Developers use it for all sorts of time-related purposes.

A chrono::duration object relies on a second as the standard duration between ticks. A tick is a single time duration interval. Using the standard setup, each tick equals one second. However, you can use the ratio object to define a new tick duration. For example, if you define ratio<60> each tick lasts one minute. Likewise, defining ratio<1, 5> sets each tick to last one fifth of a second.

It’s also possible to change one interval to another using duration_cast with either a standard interval, such as chrono::seconds, or any interval typedef that you want to create. For example, typedef chrono::duration<double, ratio<1, 5>> fifths; defines an interval called fifths.

There is a lot more to talk about with the chrono::duration class, but you now have enough information to work with the Duration example shown. As with previous examples, this one shows a progression from defining a variable directly, to using a custom UDL, and finally the built-in support that C++ 14 provides.

#include <iostream>
#include <chrono>
using namespace std;
chrono::duration<unsigned long long> operator" _m(
   unsigned long long Value)
{
    return chrono::duration<int, ratio<60>>(Value);
}
int main()
{
    // Define an interval of 20 minutes.
    chrono::duration<int, ratio<60>>StdTime(20);
    auto AutoTime(20_m);
    // Remove comment when your compiler supports C++ 14.
    //auto UDLTime(20min);
    // Output the time in seconds.
    cout << chrono::duration_cast<chrono::seconds>(StdTime).count()
        << endl;
    cout << chrono::duration_cast<chrono::seconds>(AutoTime).count()
        << endl;
    // Remove comment when your compiler supports C++ 14.
    //cout << chrono::duration_cast<chrono::seconds>(UDLTime).count()
    //    << endl;
    return 0;
}

The example demonstrates a few features of the chrono::duration class. However, it focuses again on the progression from defining the variable by hand to using a shortcut to perform the task. Notice that the UDL relies on an integer value in this case, rather than a floating-point type. The value of 20 minutes is converted to seconds for output. As a result, you see these values when you run the application:

1200
1200
1200

The Standard Library supports a number of suffixes for chrono::duration when you use C++ 14. The following list shows the individual suffixes and tells you what they mean:

  • h: Hours

  • min: Minutes

  • s: Seconds

  • ms: Milliseconds

  • us: Microseconds

  • ns: Nanoseconds