InitializeSahib
InitializeSahib

Reputation: 295

How to create a strong typedef in C++(11) without an external library?

So, I know you can alias types with the following:
typedef int *intPtr
But the C++ compiler can't differentiate between them:

typedef int foo  
typedef int bar  
foo x = 5;  
bar y = 7;  
int z = x + y;  // checks out  

I am aware that C++ does not have this without some trickery (How can I create a new primitive type using C++11 style strong typedefs?), but I found said trickery hard to read and understand.
The only plausible solution I have found is to use the Boost library, but I have a strong distaste in using external libraries.
So, is there any easily-understandable trick to make a strong typedef?

Upvotes: 2

Views: 2128

Answers (2)

Cheers and hth. - Alf
Cheers and hth. - Alf

Reputation: 145279

One way to create a type that acts like a new integral type is to use a based enum. But beware: an enum is not formally an integral type. It's only its base type that is an integral type.

Also, a heads-up: I've never found this useful enough to do it.

That said, it can go like this:

#include <type_traits>  // std::underlying_type
#include <utility>      // std::enable_if

//----------------------------------------- Machinery:

namespace cppx {
    using std::enable_if_t;
    using std::underlying_type_t;

    namespace impl {
        inline constexpr auto is_a_typed_integer( ... ) -> bool { return false; }
    }  // namespace impl

    template< class Type >
    inline constexpr auto is_typed_integer()
        -> bool
    {
        using impl::is_a_typed_integer;
        return is_a_typed_integer( Type() );
    }

    namespace enum_arithmetic {
        template< class Enum
            , class Enabled_ = enable_if_t< is_typed_integer<Enum>(), void >
            >
        inline auto operator+( Enum const a, Enum const b )
            -> Enum
        {
            using Integral = underlying_type_t<Enum>;
            return Enum( Integral( a ) + Integral( b ) );
        }
    }

}  // namespace cppx


//----------------------------------------- Usage:

namespace my {
    using cppx::enum_arithmetic::operator+;  // Makes it accessible via ADL.

    enum class Foo: int {};
    inline constexpr auto is_a_typed_integer( Foo ) -> bool { return true; }

    enum class Bar: int {};
}  // namespace my


//----------------------------------------- Test:

#include <iostream>
using namespace std;
auto main() -> int
{
    auto x = my::Foo( 5 );
    auto y = my::Bar( 7 );
    (void) y;

#ifdef TRY_CONVERSION
    int z = x + y;              //! No implicit conversion.
    (void) z;
#endif

    cout << boolalpha;
    cout << "Foo -> " << cppx::is_typed_integer<my::Foo>() << endl;     // true
    cout << "Bar -> " << cppx::is_typed_integer<my::Bar>() << endl;     // false

    auto x2 = x + x;
    cout << int( x2 ) << endl;
#ifdef TRY_BAR
    auto y2 = y + y;            //! Not an arithmetic type.
    (void) y2;
#endif
}

Upvotes: 2

wally
wally

Reputation: 11002

A typedef or using statement will not introduce a new type.

To get a new type you need to define one:

struct foo { int x; };
struct bar { int x; };

int main()
{
    //typedef int foo;
    //typedef int bar;
    foo x{5};
    bar y{7};
    int z = x + y;  // now doesn't compile, wants an operator+() defined  
    return 0;
}

In the above example we take advantage of aggregate initialization to allow for the use of the structs in this way.

Upvotes: 4

Related Questions