2

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?

Community
  • 1
  • 1
InitializeSahib
  • 295
  • 4
  • 15
  • 1
    Why not create a class for each? – wally Apr 11 '16 at 02:27
  • 6
    "I have a strong distaste in using external libraries" -- Why? That sounds like serious case of Not Invented Here syndrome. Reusing already written (not to mention reviewed and tested) code saves you time to solve the problems that haven't been solved yet, rather than reinventing the wheel a millionth time. – Dan Mašek Apr 11 '16 at 02:27
  • 4
    Is there a specific problem you're trying to solve? – wally Apr 11 '16 at 02:29
  • There are no strong typedefs in C++. – n. m. could be an AI Apr 11 '16 at 04:19

2 Answers2

4

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.

Community
  • 1
  • 1
wally
  • 10,717
  • 5
  • 39
  • 72
2

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
}
Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331