Named Types in C++

A strong type or named type is a type used in place of another type to carry specific meaning through its name.

Its central piece is the templated class NamedType, which can be used to declare a strong type with a typedef-like syntax:

using Width = NamedType<double, struct WidthTag>;
using Height = NamedType<double, struct HeightTag>;

which can be used to make interfaces more expressive and more robust. Note how the below constructor shows in which order it expects its parameters:

class Rectangle
{
public:
    Rectangle(Width w, Height h) : width(w.get()), height(h.get()) {}
    // ...
private:
    double width;
    double height;
};

Implemention

This is how you can write a class:

struct Default_NamedType_tag {};

template <typename T, typename TAG = Default_NamedType_tag>
class NamedType
{
public:
    explicit NamedType(T const& value) : value_(value) {}
    explicit NamedType(T&& value) : value_(std::move(value)) {}
    T&       get()       { return value_; }
    T const& get() const {return value_; }
private:
    T value_;
};


        


      

Strong typing over generic types

This implementation of strong types can be used to add strong typing over generic or unknown types such as lambdas:

template<typename Function>
using Comparator = NamedType<Function, struct ComparatorTag>;

template <typename Function>
void performAction(Comparator<Function> comp)
{
    comp.get()();
}

performAction(make_named<Comparator>([](){ std::cout << "compare\n"; }));

Strong typing over references

The NamedType class is designed so that the following usage:

using FamilyNameRef = NamedType<std:string&, struct FamilyNameRefTag>;

behaves like a reference on an std::string, strongly typed.

Named arguments

By their nature strong types can play the role of named parameters:

using FirstName = NamedType<std::string, struct FirstNameTag>;
using LastName = NamedType<std::string, struct LastNameTag>;

void displayName(FirstName const& theFirstName, LastName const& theLastName);

// Call site
displayName(FirstName("John"), LastName("Doe"));

But the nested type argument allows to emulate a named argument syntax:

using FirstName = NamedType<std::string, struct FirstNameTag>;
using LastName = NamedType<std::string, struct LastNameTag>;

static const FirstName::argument firstName;
static const LastName::argument lastName;

void displayName(FirstName const& theFirstName, LastName const& theLastName);

// Call site
displayName(firstName = "John", lastName = "Doe");