User-Defined Literals and Template Classes

How do user-defined literals work with template classes?

User-defined literals can be used with template classes to create flexible and reusable code. Here's how you can integrate user-defined literals with template classes:

Defining Template Classes

First, let's define a template class Quantity that can represent different types of measurements (e.g., distance, weight, etc.):

#include <iostream>

template <typename Unit>
class Quantity {
 public:
  explicit Quantity(float value)
    : value{value} {}
  float value;
};

template <typename Unit>
std::ostream& operator<<(
  std::ostream& os, const Quantity<Unit>& q) {
  os << q.value << " " << Unit::name();
  return os;
}

Defining User-Defined Literals

Next, we'll define user-defined literals for specific units. Each unit will be a class with a static method name() to provide the unit's name:

#include <iostream>

class Quantity {/*...*/}; struct Meters { static std::string name() { return "meters"; } }; struct Kilometers { static std::string name() { return "kilometers"; } }; Quantity<Meters> operator""_m(long double val) { return Quantity<Meters>{static_cast<float>(val)}; } Quantity<Kilometers> operator""_km(long double val) { return Quantity<Kilometers>{static_cast<float>(val)}; } int main() { auto distance1 = 5.0_m; auto distance2 = 3.5_km; std::cout << distance1 << "\n"; std::cout << distance2 << "\n"; }
5 meters
3.5 kilometers

Benefits of Using Template Classes

  • Type Safety: Template classes ensure that operations are type-safe. For example, you cannot mistakenly add distances and weights.
  • Reusability: You can define new units easily by creating new unit structs and corresponding user-defined literals.

Combining with Other Templates

You can combine user-defined literals with other templates, such as conversion functions. The following example has a convertTo() template function within the Quantity template class.

We then use template specialization to provide an implementation of that function for converting kilometers to meters:

#include <iostream>
#include <string>

template <typename Unit>
class Quantity {
 public:
  explicit Quantity(float value)
    : value{value} {}
  float value;

  float getValue() const { return value; }

  template <typename U>
  Quantity<U> convertTo() const;
};

template <typename Unit>
std::ostream& operator<<(
  std::ostream& os, const Quantity<Unit>& q) {
  os << q.value << " " << Unit::name();
  return os;
}

struct M {
  static std::string name() {
    return "meters";
  }
};

struct KM {
  static std::string name() {
    return "kilometers";
  }
};

template <>
template <>
Quantity<M> Quantity<KM>::convertTo<M>() const {
  return Quantity<M>{value * 1000};
}

Quantity<M> operator""_m(long double val) {
  return Quantity<M>{static_cast<float>(val)};
}

Quantity<KM> operator""_km(long double val) {
  return Quantity<KM>{static_cast<float>(val)};
}

int main() {
  auto distance1 = 5.0_m;
  auto distance2 = 1.5_km;

  auto distance_m = distance2.convertTo<M>();

  std::cout << distance1 << "\n";
  std::cout << distance2 << "\n";
  std::cout << distance_m << "\n";
}
5 meters
1.5 kilometers
1500 meters

Conclusion

Using user-defined literals with template classes enhances the expressiveness and reusability of your code.

Template classes provide a type-safe way to manage different units and operations, while user-defined literals offer a convenient syntax for creating these objects. This combination is powerful for writing clear, maintainable, and flexible C++ code.

User Defined Literals

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

Questions & Answers

Answers are generated by AI models and may not have been reviewed. Be mindful when running any code on your device.

Custom Types and User-Defined Literals
Can user-defined literals be used with custom types?
Portability of User-Defined Literals
How can we ensure that our user-defined literals are portable across different compilers?
Underscore in User-Defined Literals
Why must user-defined literals start with an underscore?
Namespaces for User-Defined Literals
How do user-defined literals interact with namespaces?
Best Practices for User-Defined Literals
What are the best practices for using user-defined literals in large projects?
Overloading User-Defined Literals
Can user-defined literals be overloaded?
Negative Values in User-Defined Literals
How do we handle negative values in user-defined literals?
Or Ask your Own Question
Get an immediate answer to your specific question using our AI assistant