Search code examples
c++c++11templatesmetaprogrammingprimitive

how to use the type of compile time constant primitive


How to use the type of a compile time constant primitive as type declaration for other variables?

I am trying to do some template metaprogramming in c++ for SI unit conversion. It comes down to how to automatically determine which primitive precision I need after one plus operator. For example:

template<typename Precision>
class Unit {
public:
    Unit(Precision v) : value(v) {}
    Precision value;
};
template<typename Precision1, typename Precision2>
struct PrecisionTransform {
    constexpr static auto test = (Precision1)1 * (Precision2)1; // Compile time constant
    using Type = Precision1; // TODO: ideally typeof(test)
};
template<typename Precision1, typename Precision2>
Unit<PrecisionTransform<Precision1, Precision2>::Type> operator+(const Unit<Precision1>& x, const Unit<Precision2>& y)
{
    return Unit<PrecisionTransform<Precision1, Precision2>::Type>(x.value + y.value);
}

int main()
{
    Unit<double> a = 2.0;
    Unit<float> b = 1.0f;
    auto c = a + b;
    return 0;
}

or in simple terms, can some thing like this happen?

float a = 1;
typeof(a) b = 2;

It seems quite possible since I've gone this far. But I am not sure how to use


Solution

  • You almost got it. As max66 already pointed out, use decltype. First of all, you can replace your PrecisionTransform class with the follwing type alias (you have to #include <utility> for this):

    template <typename Precision1, typename Precision2>
    using TransformType = decltype(std::declval<Precision1>() * std::declval<Precision2>());
    

    The std::declval<XYZ>() is just a more generic way of saying (Precision1)1 which allows you to also use types that don't have accessible constructors (in your case irrelevant as you only use primitives).

    Your operator+ is then changed to:

    template<typename Precision1, typename Precision2>
    Unit<TransformType<Precision1, Precision2>> operator+(const Unit<Precision1>& x, const Unit<Precision2>& y)
    {
        return Unit<TransformType<Precision1, Precision2>>(x.value + y.value);
    }
    

    Note that you got a typo in your version of operator+ (both operands used Precision1).

    As you can see here, the major compilers agree on this.