One approach is a templated class that serves as the one implementation and type aliases of that class for your real types. This could look as follows:
namespace detail {
template
struct Implementation {
//do everything once
};
}
using A = detail::Implementation;
using B = detail::Implementation;
using C = detail::Implementation;
As long as each uses a different type as the template argument, each will be a unique type with the same implementation, and the real class can be hidden away in something users shouldn't touch.