简体   繁体   English

C ++矩阵模板,矩阵矩阵与矩阵数乘法之间的歧义

[英]C++ Matrix template, ambiguity between matrix-matrix and matrix-number multiplication

I'm writing a matrix template class. 我正在写一个矩阵模板类。 All went well until I was overloading the multiplication operator. 一切顺利,直到我使乘法运算符超载为止。 My class look like this: 我的课看起来像这样:

template <typename TNum> class Matrix
{
private:
    // ...
    TNum* Data;
public:
    const TMatIdx NRows; // Type TMatIdx defined somewhere else.
    const TMatIdx NCols;
    const TMatIdx Size;

    // ...
    // Matrix * matrix
    template <typename T2>
    const Matrix<TNum> operator*(const Matrix<T2>& right) const;

    // Matrix * number
    template <typename T2>
    Matrix<TNum>& operator*=(const T2& scale);
};

// Matrix * number
template <typename TNum, typename T2>
Matrix<TNum> operator*(Matrix<TNum> lhs, const T2& rhs);
// Number * matrix
template <typename TNum, typename T2>
Matrix<TNum> operator*(const T2& lhs, Matrix<TNum> rhs);

I'm hoping to cover all possible multiplication combinations among matrices and numbers with the same * operator. 我希望使用相同的*运算符覆盖矩阵和数字之间所有可能的乘法组合。

Then I wrote a small test program that multiplies two Matrix<double> s, my clang++ compiler complains about ambiguity: 然后,我编写了一个小的测试程序,将两个Matrix<double>相乘,我的clang ++编译器抱怨模棱两可:

test.cpp:46: error: ambiguous overload for 'operator*' in 'M * N'
matrix.h:225: note: candidates are: const QCD::Matrix<TNum> QCD::Matrix<TNum>::operator*(const QCD::Matrix<T2>&) const [with T2 = double, TNum = double]
matrix.h:118: note:                 QCD::Matrix<TNum> QCD::operator*(const T2&, QCD::Matrix<TNum>) [with TNum = double, T2 = QCD::Matrix<double>]
matrix.h:109: note:                 QCD::Matrix<TNum> QCD::operator*(QCD::Matrix<TNum>, const T2&) [with TNum = double, T2 = QCD::Matrix<double>]
test.cpp:52: error: ambiguous overload for 'operator*' in 'M * N'
matrix.h:225: note: candidates are: const QCD::Matrix<TNum> QCD::Matrix<TNum>::operator*(const QCD::Matrix<T2>&) const [with T2 = double, TNum = double]
matrix.h:118: note:                 QCD::Matrix<TNum> QCD::operator*(const T2&, QCD::Matrix<TNum>) [with TNum = double, T2 = QCD::Matrix<double>]
matrix.h:109: note:                 QCD::Matrix<TNum> QCD::operator*(QCD::Matrix<TNum>, const T2&) [with TNum = double, T2 = QCD::Matrix<double>]

Is it possible to overcome this ambiguity without having to explicitly write down all possible specializations for T2? 是否有可能克服这种歧义而不必明确写下T2的所有可能专业化知识?

And FYI, here's my implementation: 仅供参考,这是我的实现:

template<typename TNum> template <typename T2>
Matrix<TNum>& Matrix<TNum> ::
operator*=(const T2& rhs)
{
    for(TMatIdx i = 0; i < Size; i++)
        Data[i] *= rhs;
    return *this;
}

template<typename TNum> template <typename T2>
const Matrix<TNum> Matrix<TNum> ::
operator*(const Matrix<T2>& right) const
{
    Matrix<TNum> c(NRows, right.NCols);
    TNum sum_elems;
    for(TMatIdx i = 0; i < NRows; i++)
    {
        for(TMatIdx j = 0; j < right.NCols; j++)
        {
            sum_elems = TNum(0);
            for(TMatIdx k = 0; k < right.NRows; k++)
            {
                sum_elems += at(i, k) * right.at(k, j);
            }

            c.at(i, j) = sum_elems;
        }
    }
    return c;
}


template <typename TNum, typename T2>
Matrix<TNum> operator*(Matrix<TNum> lhs, const T2& rhs)
{
    lhs *= rhs;
    return lhs;
}

template <typename TNum, typename T2>
Matrix<TNum> operator*(const T2& lhs, Matrix<TNum> rhs)
{
    rhs *= lhs;
    return rhs;
}

I'm going to describe a solution with c++11, and then explain how to implement it in c++98. 我将描述一个使用c ++ 11的解决方案,然后解释如何在c ++ 98中实现它。

In c++11 the header <type_traits> includes type functions and type predicates. 在c ++ 11中,标头<type_traits>包括类型函数和类型谓词。 This makes enforcing constraints more convenient. 这使强制约束更加方便。

std::is_same<T1, T2>::value is true if T1 is the same type as T2 and false otherwise. 如果T1T2类型相同std::is_same<T1, T2>::value为true,否则为false。

typename std::enable_if< bool, T >::type is a well defined type T if bool is true and ill-defined otherwise. 如果bool为true,则typename std::enable_if< bool, T >::type是定义良好的类型T ,否则定义不正确。

When the compiler looks for candidate template functions and methods, it doesn't throw an error if an attempted specialization fails. 当编译器寻找候选模板函数和方法时,如果尝试的专业化失败,它不会引发错误。 It simply throws out that candidate. 它只是淘汰了那个候选人。 Which means the following code will remove the ambiguity: 这意味着以下代码将消除歧义:

template <typename TNum, typename T2>
typename std::enable_if< (!std::is_same<Matrix<TNum>, T2>::value),
Matrix<TNum> >::type operator*(const T2& lhs, Matrix<TNum> rhs);

Only declarations are considered when making this decision. 做出此决定时仅考虑声明。 The above logic is semantically reasonably, but an eyesore to read. 上面的逻辑在语义上是合理的,但是让人讨厌阅读。 So c++11 supports template aliases, and constexpr functions. 因此,c ++ 11支持模板别名和constexpr函数。

template<bool B, typename T = void>
using Enable_if = typename std::enable_if<B, T>::type;

template<typename T1, typename T2>
constexpr bool Is_same(){
  return std::is_same<T1, T2>::value;
}

The above then becomes: 上面的内容变为:

template <typename TNum, typename T2>
Enable_if<( !Is_same<Matrix<TNum>, T2>() ),
Matrix<TNum> > operator*(const T2& lhs, Matrix<TNum> rhs);

concepts will provide tools to make this more convenient. 概念将提供工具,以使其更方便。

Now if you don't have c++11, you don't get the eye-candy. 现在,如果您没有c ++ 11,那么您就不会眼前一亮。 But, Boost provides the same functions. 但是,Boost提供了相同的功能。 Suppose you don't have either, implementing them isn't terrible. 假设您也没有,实施它们并不可怕。

Compile time functions depend on multiple language rules, which makes them difficult to understand. 编译时函数取决于多种语言规则,这使它们难以理解。 We'll consider enable_if first. 我们将首先考虑enable_if We want typename enable_if<true, T>::type to be well defined but, typename enable_if<false, T>::type to be nonsense. 我们希望对typename enable_if<true, T>::type进行良好定义,但对typename enable_if<true, T>::type typename enable_if<false, T>::type作废。 We use specialization: 我们使用专业化:

template<bool B, typename T = void>
struct enable_if {
  typedef T type;
};

template<typename T>
struct enable_if<false, T> {};

notice false handles exactly half of relevant cases. 注意虚假处理的只是相关案例的一半。 It's worthwhile to chew on the above. 值得一读。

In order to implement is_same , we need a notion of true or false at compile time. 为了实现is_same ,我们需要在编译时使用true或false的概念。 We can guarantee this with static const variables. 我们可以使用静态const变量来保证这一点。 We want is_same to have a compile time true value, whenever its template arguments are the same. 每当其模板参数相同时,我们希望is_same具有编译时为true的值。 The specialization system rules handle this directly. 专业化系统规则直接处理此问题。

template<typename, typename>
struct is_same{
  static const bool value = false;
};

template<typename T>
struct is_same<T, T>{
  static const bool value = true;
};

This should do what you want. 这应该做您想要的。 Note you could abstract one step further and make another pair of structs. 请注意,您可以进一步抽象一个步骤,并制作另一对结构。

struct false_type {
  static const bool value = false;
};

struct true_type {
  static const bool value = true;
};

then is_same becomes: 然后is_same变成:

template<typename, typename>
struct is_same : false_type {};

template<typename T>
struct is_same<T, T> : true_type {};

which makes it look more like a function. 这使其看起来更像一个函数。

I prefer this to the category solution, because it's easier to separate the metaprogram into a header file. 我更喜欢类别解决方案,因为将元程序分离为头文件更容易。 You can then reuse the logic elsewhere. 然后,您可以在其他地方重用该逻辑。 Still, if you aren't using c++11 or even boost, making necessary compile time functions can be a headache. 不过,如果您没有使用c ++ 11甚至没有使用boost,那么进行必要的编译时函数可能会令人头疼。

If using complex( or any simple redesign ) satisfies your current and future requirements - prefer that. 如果使用复杂的(或任何简单的重新设计)可以满足您当前和将来的要求,请首选。 Otherwise I think this solution is reasonably future proof. 否则,我认为该解决方案可以合理地面向未来。

This may be help. 这可能会有所帮助。

#include<utility>

template<class Type>
struct test{
private:
    struct _matrix{};
    struct _scalar{};
    template<class T>
    struct category{
        typedef _scalar type;
    };
    template<class T>
    struct category<test<T>>{
        typedef _matrix type;
    };

    template<class T>
    void do_foo(T, _matrix){}

    template<class T>
    void do_foo(T, _scalar){}

public:
    //c++11
    template<class T>
    void operator*(T&& a){
        do_foo(std::forward<T>(a), category<T>::type());
    }
    //Older Compiler
    //template<class T>
    //void operator*(const T& a){
    //  do_foo(a, category<T>::type());
    //}

};

int main(){
    typedef test<int> int_matrix;
    int_matrix obj;
    obj*int_matrix();
    obj*obj;
    obj*1;
    obj*1.;

    return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM