内联成员运算符与内联运算符 C++

Inline member operators vs inline operators C++

本文关键字:运算符 C++ 成员      更新时间:2023-10-16

如果我有两个结构:

struct A
{
    float x, y;
    inline A operator*(A b) 
    {
        A out;
        out.x = x * b.x;
        out.y = y * b.y;
        return out;
    } 
}

和等效的结构

struct B
{
    float x, y;
}
inline B operator*(B a, B b) 
{
    B out;
    out.x = a.x * b.x;
    out.y = a.y * b.y;
    return out;
} 
您是否知道 B 的运算符*以

任何不同的方式编译,或者运行比 A 的运算符*慢或更快的任何原因(函数内部发生的实际操作应该无关紧要(?

我的意思是...将内联运算符声明为成员而不是成员,是否会对实际函数的速度产生任何一般影响?

我有许多不同的结构,目前遵循内联成员运算符样式......但是我想将其修改为有效的 C 代码;所以在我这样做之前,我想知道性能/编译是否会有任何变化。

按照你写它的方式,我希望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必须在调用函数之前复制该参数。两者都必须复制其右侧参数。

你的代码会好得多,并且可能会为AB实现相同的代码,如果你使用引用编写它并使其const正确:

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 可能使用较少的步骤,但无论如何它都会被指令解码器扩展到这个详细级别。关键是它都是寄存器中指针的固定偏移取消引用,这大约是最快的。优化器可以尝试更智能,跨多个寄存器实现对象,但这种优化器更难编写。(尽管我偷偷怀疑LLVM类型的编译器/优化器可以轻松进行优化,如果result只是一个未保留的临时对象。

因此,由于您使用的是 this ,因此您有一个隐式指针取消引用。但是,如果对象在堆栈上怎么办?无济于事;堆栈变量转换为堆栈指针(或帧指针,如果使用(的固定偏移量取消引用。因此,您最终会取消引用指针,除非您的编译器足够明亮,可以获取您的对象并将其分布在多个寄存器中。

随意将-S选项传递给gcc,以获取最终代码的反汇编,以查看您的案例中真正发生的事情。

你真的应该把inline-ing留给编译器。

也就是说,在类定义中定义的函数(如A(默认是inline的。A::operator *inline说明符是无用的。

更有趣的情况是,当成员函数定义在类定义之外时。在这里,如果您想向编译器提供提示(它可能会随意忽略(这是经常使用的,并且指令应该在调用者中内联编译,则需要内联。

阅读C++常见问题 9.

以下是我编写结构的方法:

struct A
{
    float x, y;
    A(float ax, float ay) : x(ax), y(ay) { }
    A operator*(const A& b) const { return b(x * b.x, y * b.y); } 
}

要回答这个问题,是的,在某些情况下,将运算符编写为成员函数可能会稍微快一点,但不足以在代码中产生明显的差异。

一些注意事项:

  1. 永远不用担心使用内联关键字。优化编译器自行决定内联的内容和不内联的内容。

  2. 使用初始化构造函数。这样做是因为他们改进了代码可读性。睡得更好,知道他们可以带来小性能优势。

  3. 尽可能经常通过常量引用传递结构。

  4. 专注于编写风格良好的代码,而不是快速编写代码。大多数代码是足够快,如果不是,可能是因为某些事情在算法或 IO 处理方面存在缺陷。