I do not think there is a way to do exactly what you are asking for without "bizarre macros". This is as far as I could get:
template<typename T, T (*op)(long double)>
struct SI
{
// ...
constexpr static T micro = op (.000001);
constexpr static T milli = op (.001);
constexpr static T kilo = op (1000);
constexpr static T mega = op (1000000);
// ...
};
struct Length
{
constexpr Length(long double d) : _d(d) { }
constexpr operator long double() { return _d; }
long double _d;
};
constexpr Length operator "" _m(long double m) {
return Length(m);
}
typedef SI<Length, ::operator "" _m> SI_Length;
int main()
{
constexpr Length l = 3 * SI_Length::kilo;
static_assert(l == 3000, "error");
}
If bizarre macros are allowed, then something like the following should do the job:
#define DEFINE_SI_MULTIPLIERS(T, unit) \
constexpr T operator "" _u ## unit(long double m) \
{ return ::operator "" _ ## unit(0.000001 * m); } \
constexpr T operator "" _m ## unit(long double m) \
{ return ::operator "" _ ## unit(0.001 * m); } \
constexpr T operator "" _k ## unit(long double m) \
{ return ::operator "" _ ## unit(1000 * m); } \
// ...
DEFINE_SI_MULTIPLIERS(Length, m)
int main()
{
constexpr Length l = 3.0_km;
static_assert(l == 3000, "error");
}