Search code examples
c++templatesreturn-valuehierarchy

Giving two template class as arguments, choose the return template class using a hierarchy order


I have a class Matrix defined as template<class T, int m_rows, int m_cols = m_rows> class Matrix and an overloaded operator of the type template <class V, int m_cols2> Matrix operator * (const Matrix<V,m_cols,m_cols2>& other). The idea is that, for example, if T is a complex and V is a double, the returning matrix should be of the type V (complex). How do I accomplish that in the code?

As of now I have

    template <class V, int m_cols2>
    Matrix operator * (const Matrix<V,m_cols,m_cols2>& other) const {
        Matrix<T,m_rows,other.m_cols> res;

        for (int i = 0; i < m_rows; ++i) {
            for (int k = 0; k < other.m_rows; ++k) {
                res[i][k] = 0;
                for (int j = 0; j < m_cols; ++j) {
                    res[i][k] += m_matrix[i][j]*other[j][k];
                }
            }
        }
        return res;
    }

But this automatically sets the matrix "res" to the type T. How can I tell that it should decide the type of res depending on the type hierarchy? For example complex > double > integer?

Thanks for your answers :)

I have no idea what to try:)


Solution

  • You could produce a matrix of a "common type":

    // note: it's best not to call template parameters "m_something", because
    //       it creates confusion with data members of the class.
    template <class OtherT, int OtherCols>
    auto operator * (const Matrix<OtherT, Cols, OtherCols>& other) const
      -> Matrix<std::common_type_t<T, OtherT>, Rows, OtherCols>> // trailing return type
    {
        // Avoid copying and pasing the return type with decltype.
        // We could also use a deduced return type (just auto, no trailing -> ...),
        // however, this wouldn't be SFINAE-friendly, i.e.: operator* gets
        // instantiated even if there is no common type.
        decltype(*this * other) result;
    
        for // ...
    
        return result;
    }
    

    If your complex class is std::complex, you can just use std::common_type like in the above example. Otherwise you may have to specialize std:common_type<Complex, ...> for your type to make it work.

    This works because a double is implicitly convertible to a std::complex<double>, but not the other way around. It also works for int and double, i.e.

    static_assert(std::is_same_v<double, std::common_type_t<int, double>>); // passes