aCuria
aCuria

Reputation: 7205

Choose correct return type of template member function

I have a template class that looks like this:

template <typename T, std::size_t M, std::size_t N> // MxN matrix with elements of type T
struct Mtx{...}

// component wise division
template <typename U> Mtx operator/(const Mtx<U, M, N> &rhs) const 
{ return componentDivide(*this, rhs); }

What is the best way to ensure that the return type of functions like operator / is "correct"?

eg:

Mtx<float> * Mtx<unsigned> = Mtx<float>
Mtx<float> * Mtx<int>      = Mtx<float>
Mtx<float> * Mtx<double>   = Mtx<double>
Mtx<double> * Mtx<float>   = Mtx<double>
Mtx<short> * Mtx<int>      = Mtx<int>

Upvotes: 3

Views: 151

Answers (2)

Quentin
Quentin

Reputation: 63124

I'd personally lean on the side of decltype(std::declval<T>() / std::declval<U>()) rather than std::common_type, because it explicitly chooses the type to mirror an actual division.

Thus:

template <typename T>
struct Mtx 
{
    T _var;

    template <typename U> 
    Mtx<decltype(std::declval<T>() / std::declval<U>())>
    operator / (const Mtx<U> &rhs) const
    {
        return this->_var / rhs._var;
    }
};

Or, by using a trailing return type so we can express it with the parameters' types:

template <typename T>
struct Mtx 
{
    T _var;

    template <typename U> 
    auto operator / (const Mtx<U> &rhs) const
        -> Mtx<decltype(this->_var / rhs._var)>
    {
        return this->_var / rhs._var;
    }
};

Upvotes: 2

JeJo
JeJo

Reputation: 32797

As @Someprogrammerdude mentioned in the comments using std::common_type should work for what you want.

#include <iostream>
#include <type_traits>

template <typename T> struct Mtx 
{
    T _var;
    template <typename U> 
    Mtx<std::common_type_t<T, U>> operator/(const Mtx<U> &rhs) const
    {
        return this->_var/rhs._var;
    }
};

int main() 
{
    Mtx<float> fObj{ 1.02f };
    Mtx<unsigned> uObj{ 1 };
    Mtx<int> iObj{ 1 };
    Mtx<double> dObj{ 1.02 };
    Mtx<short> sObj{ 1 };
    std::cout << std::boolalpha
        << std::is_same_v< decltype(fObj / uObj), Mtx<float>> << '\n'  // Mtx<float> * Mtx<unsigned> = Mtx<float>
        << std::is_same_v< decltype(fObj / iObj), Mtx<float>> << '\n'  // Mtx<float> * Mtx<int> = Mtx<float>
        << std::is_same_v< decltype(fObj / dObj), Mtx<double>> << '\n' // Mtx<float> * Mtx<double> = Mtx<double>
        << std::is_same_v< decltype(dObj / fObj), Mtx<double>> << '\n' // Mtx<double> * Mtx<float> = Mtx<double>
        << std::is_same_v< decltype(sObj / iObj), Mtx<int>> << '\n';   // Mtx<short> * Mtx<int> = Mtx<int>
    return 0;
}

Output:

true
true
true
true
true

Upvotes: 3

Related Questions