你的代码写法让我觉得
B::operator*
的运行速度会稍微慢一些。这是因为
A::operator*
的实现方式在底层是这样的:
inline A A::operator*(A* this, A b)
{
A out;
out.x = this->x * b.x;
out.y = this->y * b.y;
return out;
}
因此,A
将其左侧参数的指针传递给函数,而在调用函数之前,B
必须复制该参数。两者都必须复制其右侧参数。
如果您使用引用并使其正确使用const
,则A
和B
实现相同的代码将更好:
struct A
{
float x, y;
inline A operator*(const A& b) const
{
A out;
out.x = x * b.x;
out.y = y * b.y;
return out;
}
}
struct B
{
float x, y;
}
inline B operator*(const B& a, const B& b)
{
B out;
out.x = a.x * b.x;
out.y = a.y * b.y;
return out;
}
由于结果是临时的(您没有返回修改后的现有对象),因此仍然需要返回对象而不是引用。
补充
然而,在B中使用const传递引用作为两个参数,由于解引用,它比A更有效吗?
首先,当您拼写出所有代码时,两者都涉及相同的解引用操作。(请记住,访问this
的成员意味着进行指针解引用。)
但即使如此,它取决于编译器的智能程度。在这种情况下,假设编译器查看您的结构并决定无法将其放入寄存器中,因为它是两个浮点数,因此将使用指针来访问它们。因此,解引用指针的情况(这是引用实现的内容)是您可以得到的最好的结果。汇编代码将类似于以下内容(这是伪汇编代码):
// Setup for the function. Usually already done by the inlining.
r1 <- this
r2 <- &result
r3 <- &b
// Actual function.
r4 <- r1[0]
r4 <- r4 * r3[0]
r2[0] <- r4
r4 <- r1[4]
r4 <- r4 * r3[4]
r2[4] <- r4
假设采用类似RISC(例如ARM)的架构。x86可能使用更少的步骤,但指令解码器会将其扩展到大致相同的详细级别。关键是它们都是在寄存器中进行固定偏移地址指针解引用,这就是速度最快的地方了。优化器可以尝试更聪明地实现对象跨越多个寄存器,但这种优化器要难得多。 (虽然我有一个小小的怀疑,如果 result
只是一个临时对象而不是保留的对象,则LLVM类型的编译器/优化器可以轻松执行该优化)。
所以,由于您正在使用this
,因此具有隐式指针解引用。 但是,如果对象在堆栈上呢?没有帮助;堆栈变量会转换为对堆栈指针(或帧指针,如果使用)进行固定偏移地址解引用。 因此,在最后某个地方,您将解引用指针,除非您的编译器足够聪明,可以将对象分散到多个寄存器中。
可以随意传递-S
选项给gcc
以获取最终代码的反汇编,以查看在您的情况下实际发生了什么。