内联成员运算符与内联运算符 C++
Inline member operators vs inline operators C++
如果我有两个结构:
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
必须在调用函数之前复制该参数。两者都必须复制其右侧参数。
你的代码会好得多,并且可能会为A
和B
实现相同的代码,如果你使用引用编写它并使其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); }
}
要回答这个问题,是的,在某些情况下,将运算符编写为成员函数可能会稍微快一点,但不足以在代码中产生明显的差异。
一些注意事项:
永远不用担心使用内联关键字。优化编译器自行决定内联的内容和不内联的内容。
使用初始化构造函数。这样做是因为他们改进了代码可读性。睡得更好,知道他们可以带来小性能优势。
尽可能经常通过常量引用传递结构。
专注于编写风格良好的代码,而不是快速编写代码。大多数代码是足够快,如果不是,可能是因为某些事情在算法或 IO 处理方面存在缺陷。
- 为什么比较运算符如此快速
- C++映射:具有自定义类的运算符[]不起作用(总是返回0)
- 使用C++中的模板和运算符重载执行矩阵运算
- 为什么这个运算符<重载函数对 STL 算法不可见?
- 增量运算符与后缀混淆
- 一个关于在C++中重载布尔运算符的问题
- 运算符C++ "delete []"仅删除 2 个前值
- 模板类无法识别友元运算符
- 我可以使用条件运算符初始化C风格的字符串文字吗
- 关闭||运算符优化
- 通过继承类使用来自不同命名空间的运算符
- C++Cast运算符过载
- 如何使用AngelScript注册SFML Vector2运算符
- 重载元组索引运算符-C++
- 如何使用重载的相等(==)运算符向测试用例添加描述
- 为什么Mat类的两个对象可以在不重载运算符+的情况下添加
- 多个If语句与使用逻辑运算符计算条件的单个语句的比较
- 布尔比较运算符是如何在C++中工作的
- 重载运算符new[]的行为取决于析构函数
- 如何防止clang格式在流运算符调用之间添加换行符<<