How it works...

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 the 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 "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 and error.

In the example from 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 above 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 define 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):
        enum class unit { kilogram, liter, meter, piece, };
  • A class template to specify quantities of a particular unit (such as 3.5 kilograms or 42 pieces):
        template <unit U> 
class quantity
{
const double amount;
public:
constexpr explicit quantity(double const a) :
amount(a) {}

explicit operator double() const { return amount; }
};
  • The operator+ and operator- functions for the quantity class template in order to be able to add and subtract quantities:
        template <unit U> 
constexpr quantity<U> operator+(quantity<U> const &q1,
quantity<U> const &q2)
{
return quantity<U>(static_cast<double>(q1) +
static_cast<double>(q2));
}

template <unit U>
constexpr quantity<U> operator-(quantity<U> const &q1,
quantity<U> const &q2)
{
return quantity<U>(static_cast<double>(q1) -
static_cast<double>(q2));
}
  • 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:
        namespace unit_literals 
{
constexpr quantity<unit::kilogram> operator "" _kg(
long double const amount)
{
return quantity<unit::kilogram>
{ static_cast<double>(amount) };
}

constexpr quantity<unit::kilogram> operator "" _kg(
unsigned long long const amount)
{
return quantity<unit::kilogram>
{ static_cast<double>(amount) };
}

constexpr quantity<unit::liter> operator "" _l(
long double const amount)
{
return quantity<unit::liter>
{ static_cast<double>(amount) };
}

constexpr quantity<unit::meter> operator "" _m(
long double const amount)
{
return quantity<unit::meter>
{ static_cast<double>(amount) };
}

constexpr quantity<unit::piece> operator "" _pcs(
unsigned long long const amount)
{
return quantity<unit::piece>
{ static_cast<double>(amount) };
}
}

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; that 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; that 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:

    using namespace units; 
using namespace unit_literals;

auto q1{ 1_kg }; // OK
auto q2{ 4.5_kg }; // OK
auto q3{ q1 + q2 }; // OK
auto q4{ q2 - q1 }; // OK

// error, cannot add meters and pieces
auto q5{ 1.0_m + 1_pcs };
// error, cannot have an integer number of liters
auto q6{ 1_l };
// error, can only have an integer number of pieces
auto q7{ 2.0_pcs}

q1 is a quantity of 1 kg; that 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; that is a real value. Since an overload 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 a 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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset