Adam Stepniak
Adam Stepniak

Reputation: 877

'Overloading' function without input parameters based on template types

I'm started playing around with templates and came accross a problem with a need to call a function based on 2 template parameter types. I'm writting simple unit library that will have helper getConvertionFactor functions that will define ratio between units. I'd like to be able to pickup a function in template operators +,-,*,/ defintion based on types beeing pased as arguments. I know it's possible when these arguments are passed futher to getConvertionFactor function. Then overload resolution will pickup function that has specified ratio between given arguments type. But this approach as I undestand will result with unnecessary copying of LHS and RHS parameters into getConvertionFactor function just to perform overload resolution. I'd like to avoid it and perform somehow lookup of a function based on 2 types without need to pass dummy parameters into it.

#include <iostream>

struct Kilometer {
    int val = 0;
};
struct NauticalMile {
    int val = 0;
};

template<class FromType, class ToType>
double getConvertionFactor() {
    return FromType::getBaseValue();
}

// How to define such conceptually function pointer lookup based on these 2 types?
// double getConvertionFactor<Kilometer, NauticalMile>() {
//     return 0.1;
// }

// Works but requires copying parameters
double getConvertionFactor(Kilometer km, NauticalMile /*nm*/) {
    return 0.1 * km.val;
}

template<class LHS_Unit, class RHS_Unit>
LHS_Unit operator+(const LHS_Unit& nm, const RHS_Unit& km) {
    LHS_Unit lhs;
    lhs.val = nm.val + getConvertionFactor(nm, km)*km.val;
    return lhs;
}

int main() {
    Kilometer km{100};
    NauticalMile nm{100};

    auto res = km + nm;
    std::cout << res.val << std::endl;

    return 0;
}   

Upvotes: 0

Views: 857

Answers (1)

rustyx
rustyx

Reputation: 85371

You can do it like this:

template<class FromType, class ToType>
double getConvertionFactor() {
    return 1;
}

template<>
double getConvertionFactor<Kilometer, NauticalMile>() {
    return 1.852;
}

template<>
double getConvertionFactor<NauticalMile, Kilometer>() {
    return 1 / 1.852;
}

template<class LHS_Unit, class RHS_Unit>
LHS_Unit operator+(const LHS_Unit& lhs, const RHS_Unit& rhs) {
    return { lhs.val + getConvertionFactor<LHS_Unit, RHS_Unit>() * rhs.val };
}

But it might be a good idea to implement user-defined conversion operators so that the types are implicitly convertible to one another.

#include <iostream>

struct Kilometer;
struct NauticalMile;

struct Kilometer {
    double val = 0;

    operator NauticalMile() const;
};
struct NauticalMile {
    double val = 0;

    operator Kilometer() const;
};

Kilometer::operator NauticalMile() const {
    return { val / 1.852 };
}

NauticalMile::operator Kilometer() const {
    return { val * 1.852 };
}

template<class LHS_Unit, class RHS_Unit>
LHS_Unit operator+(const LHS_Unit& lhs, const RHS_Unit& rhs) {
    return { lhs.val + static_cast<LHS_Unit>(rhs).val };
}

int main() {
    Kilometer km{ 100 };
    NauticalMile nm{ 100 };

    auto res = km + nm;
    std::cout << res.val << std::endl;

    return 0;
}

Upvotes: 1

Related Questions