提问人:yomol777 提问时间:4/28/2023 最后编辑:Remy Lebeauyomol777 更新时间:4/29/2023 访问量:104
如何使右值在 c++ 中表现得像左值引用?
How to make rvalue behave like lvalue reference in c++?
问:
我目前正在用C++编写一个简单的代数库。该库有一个类,定义如下:Matrix
template<typename T>
class Matrix {
private:
size_t n, m;
T **const tab;
public:
Matrix(const size_t n, const size_t m);
Matrix(const Matrix&);
~Matrix(void);
RowVector<T> operator[](const int i);
const RowVector<T> operator[](const int i) const;
...
};
该代码很智能,它不仅允许您访问矩阵的某些特定元素(例如),而且还允许您用作向量。M[2][3]
M[2]
但是,作为优化,数据不会复制到向量中。 通过指向内存内部的 .如果调用每次都会构造一个新的向量,那么访问一个矩阵元素将是非常无效的。Vector
operator[]
Matrix
operator[]
这也使得分配矩阵元素成为可能,如下所示:
M[5][7] = 12;
但是,我对这个解决方案有两个问题:
- 返回值 是右值,不能像这样设置矩阵的整行:
operator[]
Matrix M(2, 3);
RowVector v(3);
M[0] = v;
- 当我输入这样的内容时:
v = M[7];
我想调用 's copy 构造函数,而不是 move 构造函数。但是 的返回值是一个右值,编译器将调用它的移动构造函数。RowVector
operator[]
综上所述:
RowVector
是一个右值,但它包含指向 的内部数据的指针,我希望将其视为左值引用。Matrix
这在 C++ 中可能吗?如果没有,有人可以建议对此代码进行一些重构以实现我想要的吗?
编辑:
RowVector
声明:
template<typename T>
class Vector {
protected:
const size_t len;
T *const tab;
bool malloced;
public:
Vector(const size_t len);
Vector(T *const tab, const size_t len);
virtual ~Vector(void);
T &operator[](const int i);
const T &operator[](const int i) const;
};
template<typename T>
class RowVector : public Vector<T> {
public:
RowVector(const size_t len);
RowVector(T *const tab, const size_t len);
~RowVector(void) override;
template<typename U> friend
std::ostream &operator<<(std::ostream&,
const RowVector<U>&);
};
Vector
定义:
template<typename T>
Vector<T>::Vector(const size_t len) :
len(len),
tab(new T[len]),
malloced(true)
{
}
template<typename T>
Vector<T>::Vector(T *const tab, const size_t len) :
len(len),
tab(tab),
malloced(false)
{
}
template<typename T>
Vector<T>::~Vector(void)
{
if (malloced)
delete tab;
}
template<typename T>
T &Vector<T>::operator[](const int i)
{
return tab[i];
}
template<typename T>
const T &Vector<T>::operator[](const int i) const
{
return tab[i];
}
Matrix<T>::operator[]()
:
template<typename T>
RowVector<T> Matrix<T>::operator[](int i)
{
return RowVector(tab[i], m);
}
template<typename T>
const RowVector<T> Matrix<T>::operator[](int i) const
{
return RowVector(tab[i], m);
}
答:
首先,我同意@StoryTeller,最好将其拆分为 RowVector 和 RowReference 类。但是,我仍然会继续回答您最初的问题。
好的,让我们逐个回顾一下。
- operator[] 的返回值是一个右值,不能像这样设置矩阵的整行:
Matrix M(2, 3);
RowVector v(3);
M[0] = v;
这实际上不是这里的问题。如果函数是类类型,则将其分配给函数的临时返回值是完全有效的。但是,您可能遇到问题的原因是 RowVector 类定义中有 const 成员,即 和 。在 C++ 中,默认赋值运算符等效于说类似tab
len
=
FOR member IN this
member = other.member
END
考虑到这一点,尝试这样做是无效的,因为 len 是 .因此,C++ 禁用任何具有成员的类的赋值运算符。如果您想定义自己的赋值运算符,您可以完全自由地这样做。我想你会看到类似的东西len = other.len
const
const
RowVector<T>& operator=(const RowVector<T>& other){
if (len != other.len)
throw std::runtime_error("Cannot assign rows with different lengths!");
std::copy(other.tab, other.tab + len, tab);
return *this
}
这应该允许您设置整行。
- 当我输入这样的内容时:
v = M[7];
我希望调用 RowVector 的复制构造函数,而不是移动构造函数。但是 operator[] 的返回值是一个右值,编译器将调用它的移动构造函数。
我不相信移动构造函数会在这里发挥作用。对于定义自定义复制构造函数、复制赋值运算符、移动构造函数、移动赋值运算符或析构函数的任何类,都会自动禁用默认移动构造函数。由于 RowVector 定义了自定义析构函数,因此不应定义默认的移动构造函数,而且看起来您没有自己定义一个。这个问题可能又回到了赋值运算符的删除,这将再次通过定义自定义赋值运算符来解决。
稍微切线一点,将矩阵数据存储为双指针并不是创建矩阵类的最有效方法。如果你追求效率,那么你总是希望以连续的内存分配为目标,在这种情况下,这将是 n x m 的大小,并且你会在 .当然,这也会搞砸你现有的向量类,所以如果你想走这条路,我就把它留给你了。T**
std::vector
operator[]
评论
RowVector<T>
RowReference
RowVector
RowVector<T>::operator=
Matrix<T>::operator[]