我有一个结构,例如:
struct A {
double x,y;
vector<double> vec;
};
我想重载加号等运算符,以便我可以执行以下操作:
A a,b,c,d;
//do work to set up the four structs. Then:
d = a + b + c;
性能很重要,因为这些操作将在运行多次的“内部循环”中执行。我担心该行 (a + b + c) 将创建临时对象,因此不必要地将构造函数运行到 'A'。 运行 C++17,编译器一定会使用复制省略来避免创建临时文件吗?我需要运行“d=a+b+c”这一行,而不必运行 A 的构造函数。 >
我知道如果我可以通过写作来避免临时性:
d = a;
d += b;
d += c;
但是,实际上,我将要编写很多带有很长的数学行的代码,并且能够在一行中编写所有内容 (a + b + c) 会方便得多,而不是将其分解为大量的“+=”行。
答案 0 :(得分:2)
正如评论者所建议的那样,如果您的 operator+
需要一个临时的,您仍然可以构造一个 vector
并返回它,即使 NRVO 已被拒绝。
但是,如果你想这样做,你可以减少临时创建的数量:
operator+
实现d
中,或使用移动构造函数将临时向量移动到 d
中,而无需考虑一下:
A operator+(A& a, const A& b){
A temp = /*...*/;
return temp; // NRVO
}
A operator+(A&& a, const A& b){
// We know a is temporary, so we can move its guts to a new one.
a += b;
return std::move(a); // In this case, we require the move, as it's not NRVO
}
// Then use it:
A d = a + b + c;
// What this does:
// 1. Calls `a + b` and creates a temporary, as both are lvalues
// 2. Utilizes the same temporary to put the result of (a+b) + c
// 3. Still utilizes the same temporary to materialize the rvalue into d
答案 1 :(得分:0)
感谢 Joel Filho 建议使用表达式模板,以及对相关 Wikipedia article 的参考。维基百科文章中的这种方法有效,尽管必须针对我的特定情况稍作修改,因为我使用的是命名的类成员,而不是实现向量。下面是一个实现。
template<typename E>
class AExpression {
public:
AExpression() {};
double Returna() const {
return static_cast<E>(*this).Returna();
};
double Returnb() const {
return static_cast<E>(*this).Returnb();
};
};
struct A : public AExpression<A> {
A() : a(kNaN), b(kNaN) { };
double a,b;
double Returna() const {
return a;
};
double Returnb() const {
return b;
};
template <typename E>
A(AExpression<E> & expr) {
a = expr.Returna();
b = expr.Returnb();
};
//this method is needed because the return value of a line such as z = x + y is ASum, not A. This equality overload allows the code to convert back from type ASum to type A
template <typename E>
A & operator=(E const & expr) {
a = expr.Returna();
b = expr.Returnb();
return *this;
}
};
template <typename E1, typename E2>
class ASum : public AExpression<ASum<E1, E2>> {
E1 const& one;
E2 const& two;
public:
ASum(E1 const& onein, E2 const& twoin) : one(onein), two(twoin) { };
double Returna() const {
return one.Returna() + two.Returna();
};
double Returnb() const {
return one.Returnb() + two.Returnb();
};
};
template <typename E1, typename E2>
ASum<E1, E2>
operator+(AExpression<E1> const& u, AExpression<E2> const& v) {
return ASum<E1, E2>(*static_cast<const E1*>(&u), *static_cast<const E2*>(&v));
}
使用该实现,如果我运行以下几行,它会执行“z = x + y”而不创建任何临时文件。
A x,y,z;
x.a = 1;
x.b = 2;
y.a = 3;
y.b = 4;
z = x + y;
//z is type 'A' and z.a = 4; z.b = 6; no temporaries are created in the line 'z = x + y'