【问题标题】:Boost operators with mixed types - conversion and private membersBoost 混合类型的运算符 - 转换和私有成员
【发布时间】:2024-04-21 23:20:02
【问题描述】:

我正在使用Boost-Operatators 来构造一个矩阵类。 (一个玩具项目)。但是,当我想混合不同元素类型的矩阵时遇到了问题。

基本上我有一个模板类Matrix<T>,其中T 是该矩阵的元素类型。我正在使用 Boost-Operators 来定义 Matrix<T> 实例之间的运算符(例如元素相加),Matrix<T>T 之间(例如标量乘法),如果可能的话也在 Matrix<T>Matrix<U> 之间定义运算符(例如实矩阵加复矩阵)。

boost 运算符支持一个或两个模板参数。如果您想要两个相同类型的对象之间的运算符,则一个,如果您想要混合运算符,则两个。

template<typename T>
class Matrix : boost::addable<Matrix<T>> // Add another matrix of same type.
               boost::multiplyable2<Matrix<T>,T> // Scalar multiplication with a `T`.

但是,我不能将Matrix&lt;U&gt; 作为第二个参数,因为这样我的类将有两个模板参数,并且类型将取决于我可以使用哪些矩阵。

template<typename T, typename U>
class Matrix : boost::addable2<Matrix<T,U>,Matrix<U,?>> // Now I have two template arguments.
                                                        // That's certainly not what I want!

我也尝试实现我自己的boost::addable 版本,但这也不起作用。编译器抱怨一个不完整的类型。

template<class Derived>                                       
class Addable {                                               
    template<class Other>                                     
    friend Derived operator+(Derived lhs, const Other &rhs) { 
        return lhs += rhs;                                    
    }                                                         

    template<class Other>                                     
    friend Derived operator+(const Other &lhs, Derived rhs) { 
        return rhs += lhs;                                    
    }                                                         
};                                                            

另一种方法是定义一个从Matrix&lt;U&gt;Matrix&lt;T&gt; 的转换构造函数。但是,现在我有一个问题,这是两种不同的类型,我无法访问私人成员。所以,我要么需要公开比我想要的更多的东西,要么找到一种不同的方式来做这件事。

你会如何实现这样的事情?

完整代码

#include <cassert>
#include <utility>
#include <complex>
#include <vector>
#include <algorithm>
#include <iostream>

#include <boost/operators.hpp>


typedef double Real;
typedef std::complex<Real> Complex;


template<typename T>
class Matrix : boost::addable<Matrix<T>>
{
public:
    Matrix() = default;
    template<typename U>
    Matrix(const Matrix<U> &other)
        : m_(other.m()), n_(other.n()),
          data_(other.data_.begin(), other.data_.end()) { }
    Matrix(size_t m, size_t n) : m_(m), n_(n), data_(m*n) { }
    Matrix(size_t m, size_t n, const T &initial)
        : m_(m), n_(n), data_(m*n, initial) { }

    size_t m() const { return m_; }
    size_t n() const { return n_; }
    size_t size() const {
        assert(m_*n_ == data_.size());
        return data_.size();
    }

    const T &operator()(size_t i, size_t j) const { return data_[i*m_ + j]; }
    T &operator()(size_t i, size_t j) { return data_[i*m_ + j]; }

    void fill(const T &value) {
        std::fill(data_.begin(), data_.end(), value);
    }

    Matrix &operator+=(const Matrix &other) {
        assert(dim_match(other));
        for (int i = 0; i < size(); ++i) {
            data_[i] += other.data_[i];
        }
        return *this;
    }

    friend std::ostream &operator<<(std::ostream &o, const Matrix &m) {
        if (m.size() == 0) {
            o << "()" << std::endl;
            return o;
        }
        for (int i = 0; i < m.m(); ++i) {
            o << "( ";
            for (int j = 0; j < m.n() - 1; ++j) {
                o << m(i,j) << ", ";
            }
            o << m(i, m.n() - 1) << " )" << std::endl;
        }
        return o;
    }

private:
    bool dim_match(const Matrix &other) {
        return n_ == other.n_ && m_ == other.m_;
    }

private:
    int m_, n_;
    typedef std::vector<T> Store;
    Store data_;
};


int main() {
    Matrix<Real> A(2,3, 1.);
    Matrix<Complex> B(2,3, Complex(0,1));
    auto C = Matrix<Complex>(A) + B;
    std::cout << A << std::endl;
    std::cout << B << std::endl;
    std::cout << C << std::endl;
}

【问题讨论】:

  • 首先,你要让operator+=返回一个值(-Wall -Wextra for you)
  • @sehe 谢谢,我修正了错字。不幸的是,它并没有解决问题。
  • +1 提出了一个很好的问题。我必须说,如果 SO 上的所有示例代码都那么干净,我真的很喜欢 :)
  • @sehe 谢谢!我试图让事情变得可读。 ;)

标签: c++ boost


【解决方案1】:

我会这样做:使用朋友模板功能(参见Operator overloading:会员和非会员之间的决定):

template<typename T>
class Matrix
{
public:
    template<typename> friend class Matrix;

后来

template <typename T1, typename T2>
Matrix<typename std::common_type<T1, T2>::type> 
    operator+(Matrix<T1> const& a, Matrix<T2> const& b)
{
    Matrix<typename std::common_type<T1, T2>::type> result(a);
    return (result += b);
}

注意使用common_type 来获得合理的结果类型(您可能希望在此处引入自己的特征以满足您的特定要求)

Live On Coliru

【讨论】:

  • 修复了示例,现在它实际上 /adds/ 数组 :)
  • 感谢您的回答。看来您创建了data_ 成员public 来解决问题。虽然这可行,但我认为这不是一个好主意。矩阵如何存储它的元素应该是特定于实现的,而不是接口的一部分......
  • 我想我现在明白了。您可以将另一个矩阵加为好友template&lt;typename T2&gt; class Matrix;。然后,即使private data_ 成员也可以进行转换。在这种情况下,operator+ 甚至不再需要成为朋友。无论如何,std::common_type 是关键!谢谢!
  • @Lemming Erm。是的。我不知道我是怎么忘记解决这个问题的。我首先专注于返回类型 - 并“停放”了朋友的东西片刻。无论如何,修复了我与私人成员合作的答案:)
  • 不用担心。 =) 感谢您的帮助!
最近更新 更多