我不想在代码中重复我自己,但是我也不想因为简单的功能而失去性能。假设该类具有operator+
和函数Add
具有相同的功能(将前者视为在表达式中使用类的便捷方式,而后者则作为“灵活”的方式)
struct Obj {
Obj operator+(float);
Obj Add(float);
/* some other state and behaviour */
};
Obj AddDetails(Obj const& a, float b) {
return Obj(a.float_val + b, a.some_other_stuff);
}
Obj Obj::operator+(float b) {
return AddDetails(*this, b);
}
Obj Obj::Add(float b) {
return AddDetails(*this, b);
}
为了使更改更容易,两个功能都通过辅助功能调用来实现。因此,打给话务员的任何电话都会打2个电话,这确实不是很愉快。
但是编译器是否足够聪明,可以消除此类重复调用?
我用简单的类(包含内置的类型和指针)进行了测试,优化器只是不计算不需要的东西,但是它在大型系统中的表现如何(尤其是热调用)?
如果这是发生RVO的地方,那么它是否可以在较大的通话序列(3-4)中工作以将其折叠为1个通话?
P.S。是的,是的,过早的优化是万恶之源,但我仍然想要一个答案
答案 0 :(得分:2)
是,请参阅在https://godbolt.org/z/VB23-W第21行上生成的说明叮当声
movsd xmm0, qword ptr [rsp] # xmm0 = mem[0],zero
addsd xmm0, qword ptr [rip + .LCPI3_0]
它只需要直接应用AddDetails
的代码,而不用甚至调用您的操作员+。这称为内联,甚至对于这组价值返回调用也有效。
不仅RVO优化可以发生在单行函数上,而且其他所有优化(包括内联)也可以参见https://godbolt.org/z/miX3u1和https://godbolt.org/z/tNaSW。
看看这一点,您可以看到gcc和clang甚至对非内联声明的代码(https://godbolt.org/z/8Wf3oR)都进行了很大的优化
#include <iostream>
struct Obj {
Obj(double val) : float_val(val) {}
Obj operator+(float b) {
return AddDetails(*this, b);
}
Obj Add(float b) {
return AddDetails(*this, b);
}
double val() const {
return float_val;
}
private:
double float_val{0};
static inline Obj AddDetails(Obj const& a, float b);
};
Obj Obj::AddDetails(Obj const& a, float b) {
return Obj(a.float_val + b);
}
int main() {
Obj foo{32};
Obj bar{foo + 1337};
std::cout << bar.val() << "\n";
}
即使没有内联也无法通过
看到额外的C-Tor调用#include <iostream>
struct Obj {
Obj(double val) : float_val(val) {}
Obj operator+(float);
Obj Add(float);
double val() const {
return float_val;
}
private:
double float_val{0};
static Obj AddDetails(Obj const& a, float b);
};
Obj Obj::AddDetails(Obj const& a, float b) {
return Obj(a.float_val + b);
}
Obj Obj::operator+(float b) {
return AddDetails(*this, b);
}
Obj Obj::Add(float b) {
return AddDetails(*this, b);
}
int main() {
Obj foo{32};
Obj bar{foo + 1337};
std::cout << bar.val() << "\n";
}
但是,由于编译器知道该值不会更改,因此进行了一些优化,因此我们将main更改为
int main() {
double d{};
std::cin >> d;
Obj foo{d};
Obj bar{foo + 1337};
std::cout << bar.val() << "\n";
}
但是您仍然可以在两个编译器上看到优化 https://godbolt.org/z/M2jaSH和https://godbolt.org/z/OyQfJI
答案 1 :(得分:0)
据我了解,现代编译器需要在您的情况下应用复制省略。根据{{3}},当您编写return Obj(a.float_val + b, a.some_other_stuff)
时,构造函数调用是prvalue;返回它不会创建临时对象,因此不会发生移动或复制。