How best to implement the "newtype" idiom in C++?

What is the best way to implement the newtype idiom in C++?

Rating the best many times end up in the preferential domain, but you're already mentioned two alternative approaches yourself: simply custom structs wrapping a value of a common type (say int), or using enum classes with an explicitly specified underlying type for strongly type near-identical types.

If you're mainly after strongly typed type aliases of a common type, say

struct Number { int value; }

or, a common type with a parameterizable underlying type

template<typename ValueType = int>
struct Number { ValueType value; }

then another common approach (which also facilitates re-using functionality between strongly type-distinct but related types) is making(/expanding) the Number class (template) a class template parameterized over type template tag parameter, such that specializations over the tag types results in strong typing. As pointed out by @Matthieu M., we may declare a struct as part of the template argument list to a given specialization, allowing a lightweight tag declaration and alias tagging in a single alias declaration:

template<typename Tag, typename ValueType = int>
struct Number {
    ValueType value;
    // ... common number functionality.
};

using YearNumber = Number<struct NumberTag>;
using DayNumber = Number<struct DayTag>;

void takeYears(const YearNumber&) {}
void takeDays(const DayNumber&) {}

int main() {
    YearNumber y{2020};
    DayNumber d{5};
    
    takeYears(y);
    //takeDays(y);  // error: candidate function not viable
    
    takeDays(d);
    //takeYears(d);  // error: candidate function not viable
    
    return 0;
}

Note that in case you would like to specialize non-member functions of the Number class template for specific tags (or e.g. use tag dispatch for a similar purpose), you would need to declare the type tags outside of the alias declaration.


If you have boost, BOOST_STRONG_TYPEDEF does exactly what you want as already seen in this answer.


There is nothing in the c++ language (yet) that can do it directly as you want. But then again, detailed needs could be different, eg. someone might say it's ok to do an implicit construction where as another might say it has to be explicit. Due to that and other combinations1 it is difficult to provide one mechanism that will satisfy everyone and we already have normal type alias (ie. using, which ofc. is different from a strong typedef).

That being said, c++ gives you enough tools that you can build this generic tool yourself and it is not completely difficult to do if you have some experience with templates, etc..

In the end it depends on what newtype issues you actually have, eg. do you just need a handfull or are you going to make these in bulks. For something ordinary like Years and Days you could just use bare structs:

struct Days {int value;};

struct Years {int value;};

However, if you must avoid a situation like this:

bool isold(Years y);

...

isold({5});

You then must make a constructor and make it explicit, ie.:

struct Years {
   explicit Years(int i);
...

1 another combination could for example be if the new type should be allowed to convert to the underlying type, could be usefull for something like int, or it could be dangerous depending on context


I have used boost strong typedef in the past. The documentation on it seems quite sparse, but fwiw, it seems to be used by facebook, and LLVM seems to have a similar thing called LLVM_YAML_STRONG_TYPEDEF, indicating that it may have had some real-world-exposure.

Tags:

C++

Newtype