A quick way is to define each new type using BOOST_STRONG_TYPEDEF. It will implement the operators for you. Off course this "uses" preprocessor macros, but you don't have to define them. Boost.units implements arithmetic types the template way, but is a bit more involved if you want to define your own system (outside the SI system).
If you want to roll your own template classes then it becomes important to make each instantiation a unique type. There are several ways to do this. One way would be to give the template a second template argument (in addition to the "underlying type") to make instantiations unique. This second template argument could be as simple as an integer:
template< class T, int id>
struct strong_typedef {
// your operators here
};
But then you'd have to keep an administration of which integers you have been using before (which is difficult if definitions are distributed over different locations).
Alternatively you could introduce a tag type as a second parameter:
template< class T, class Tag>
struct strong_typedef// etc...
struct integerA {};
typedef strong_typedef< long, integerA> MyIntegerAType;