我正在尝试编写一个 C++ vector 类,它存储数据数组并允许在逐个元素的基础上执行数学运算。我想以表达式 a = b + c + d
的方式实现它应该只遍历所有元素一次并直接写入总和 b[i] + c[i] + d[i]
至 a[i]
无需创建中间 vector 。
我在写这样的东西:
template<class T, int N>
class VectorExpression {
public:
virtual T operator[] (int i) const = 0;
virtual ~VectorExpression() {}
}
template<class T, int N>
class MyVector : public VectorExpression<T, N> {
T data[N];
public:
T& operator[] (int i) { return data[i]; }
T& const operator[] (int i) const { return data[i]; }
MyVector<T,N>& operator=(const VectorExpression<T,N> &rhs) {
for (int i = 0; i < N; ++i)
data[i] = rhs[i];
return *this;
}
}
template<class T, int N>
class VectorSum : public VectorExpression<T, N> {
VectorExpression<T,N> &a, &b;
public:
VectorSum(VectorExpression<T,N> &aa, VectorExpression<T,N> &bb)
: a(aa), b(bb) {}
T operator[] (int i) const { return a[i] + b[i]; }
}
template<class T, int N>
VectorSum<T,N> operator+(const VectorExpression<T,N> &a,
const VectorExpression<T,N> &b)
{
return VectorSum<T,N>(a, b);
}
int main() {
MyVector<double,10> a, b, c, d;
// Initialize b, c, d here
a = b + c + d;
return 0;
}
可能这个功能是由 valarray 类提供的,但那是因为我试图将它简化为一个最小的例子。
我做了 operator[]
virtual 因为这允许嵌套所有类型的表达式(例如 a = !(-b*c + d)
),前提是我将定义所有运算符和类似于 VectorSum
的相应类.
我使用引用是因为普通变量不是多态的,而且指针不能与运算符重载一起使用。
现在我的问题是:
在声明中
a = b + c + d;
, 两个临时VectorSum<double,10>
将创建对象来存储b + c
和(b+c) + d
分别。它们的生命周期是否足以使多态行为起作用?更具体地说,(b+c) + d
将存储对b + c
的引用, 但当operator=
时该对象是否仍然存在叫做?根据this post所有临时对象都应该存在直到operator=
返回,但这是否也适用于旧版本的 C++?如果不是,那么这是怎么做到的?我看到的唯一选择是分配
VectorSum
使用new
的对象, 通过引用返回它们,然后在operator=
中删除它们功能,但这似乎有点麻烦,而且效率可能低得多。我也不确定它是否总是安全的。(小问题)是否可以覆盖返回类型
T
的VectorExpression::operator[]
通过T& const
在MyVector
?
编辑
我在 operator+ 中有错误的参数类型:将它们从 VectorSum
更改为至 VectorExpression
.
最佳答案
这就是我想出的:
#include <iostream>
#include <initializer_list>
#include <algorithm>
template<class T, int N>
class VectorExpression {
public:
virtual T operator[] (int i) = 0;
virtual const T operator[] (int i) const = 0;
virtual ~VectorExpression() {}
};
template<class T, int N>
class MyVector : public VectorExpression<T, N> {
T data[N];
public:
MyVector() {
// initialize zero
std::fill(std::begin(data), std::end(data), T());
}
MyVector(const std::initializer_list<T>& values) {
// initialize from array initializer_list
std::copy(std::begin(values), std::end(values), data);
}
MyVector(const VectorExpression<T,N>& rhs) {
for (int i = 0; i < N; ++i)
data[i] = rhs[i];
}
MyVector<T,N>& operator=(const VectorExpression<T,N>& rhs) {
for (int i = 0; i < N; ++i)
data[i] = rhs[i];
return *this;
}
T operator[] (int i) { return data[i]; }
const T operator[] (int i) const { return data[i]; }
friend std::ostream& operator<<(std::ostream& stream, MyVector& obj) {
stream << "[";
for (int i = 0; i < N; ++i) {
stream << obj.data[i] << ", ";
}
stream << "]";
return stream;
}
};
template<class T, int N>
class VectorSum : public VectorExpression<T, N> {
const MyVector<T,N> &a, &b;
public:
VectorSum(const MyVector<T,N>& aa, const MyVector<T,N>& bb):
a(aa), b(bb) {
}
T operator[] (int i) { return return a[i] + b[i]; }
const T operator[] (int i) const { return a[i] + b[i]; }
};
template<class T, int N>
MyVector<T,N> operator+(const MyVector<T,N>& a, const MyVector<T,N>& b) {
return VectorSum<T,N>(a, b);
}
int main() {
MyVector<double,3> a, b({1,2,3}), c({3,4,5}), d({4,5,6});
a = b + c + d;
std::cout << b << std::endl;
std::cout << c << std::endl;
std::cout << d << std::endl;
std::cout << "Result:\n" << a << std::endl;
return 0;
}
输出:
[1, 2, 3, ]
[3, 4, 5, ]
[4, 5, 6, ]
Result:
[8, 11, 14, ]
我添加了一个 initializer_list (C++11) 构造函数和 ostream 运算符,纯粹是为了方便/说明目的。
由于您已将 operator[] 定义为按值返回,因此我无法在数据数组中设置项目以进行测试(因为错误:左值需要作为赋值的左操作数);通常这个运算符应该是引用 - 但是,在你的情况下, VectorSum::operator[] 将不起作用,因为这会因为返回对临时对象的引用而导致编译失败。
我还添加了一个复制构造函数,因为......
// this calls MyVector's copy constructor when assigned to 'main::a'
template<class T, int N>
MyVector<T,N> operator+(const MyVector<T,N>& a, const MyVector<T,N>& b) {
return VectorSum<T,N>(a, b); // implicit MyVector::copy constructor
}
// this also calls MyVector's copy constructor (unless the copy constructor is defined explicit)
template<class T, int N>
MyVector<T,N> operator+(const MyVector<T,N>& a, const MyVector<T,N>& b) {
MyVector<T,N> res = VectorSum<T,N>(a, b);
return res;
}
// but this would call MyVector's assignment operator
template<class T, int N>
MyVector<T,N> operator+(const MyVector<T,N>& a, const MyVector<T,N>& b) {
MyVector<T,N> res;
res = VectorSum<T,N>(a, b);
return res;
}
回答您的问题:
- 是的 - 如果您显式定义变量,它会如何表现 返回那个?对于临时人员来说,这是相同的行为,除了那里 没有变量声明;
- 不适用
- 我在上面提到过这个 - 你不能 由于“返回对临时的引用”错误而使用引用; 但是,没有理由可以将 T& operator[] 添加到 MyVector(即不覆盖)。
编辑:对评论的回答:
覆盖返回类型时,函数规范必须相同。由于您已经在 VectorExpression 中定义了按值返回,因此它必须在 MyVector 中按值返回。如果您尝试将其更改为子类中的引用,则会出现编译错误:指定的返回类型冲突。所以不,你不能用返回 const T& 而不是 T 的版本覆盖运算符 const。此外,它必须按值返回,因为 MyVectorSum 返回
{ a[i] + b[i] }
这将是临时的,您不能返回对临时的引用。对不起,我的错误,已在上面修复。
因为:
- MyVector 不是 VectorSum 的子类型 - 编译错误“MyVector”不是从“const VectorSum”派生的
- 我也尝试过使用 VectorExpression 但编译错误:“无法分配抽象类型的对象”- 因为它试图按值返回
- 我选择了 MyVector,因为这是您预期结果的类型。是的,它执行了所有那些 for 循环,但我看不出解决方法:有三个不同的数组“数据”变量,每个变量都需要迭代才能累积。在代码中的某个位置,您将不得不执行 for 循环。
明白了,是的,我很困惑。从帖子中删除。
关于c++ - C++ 中的高效 vector 运算符/对临时对象的引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31240072/