我试图了解在 C++/Linux/x86-64 平台中按值传递对象作为函数参数的开销。
我用于探索的实验代码发布在下面和godbolt.org上:https://godbolt.org/z/r9Yfv4
假设函数是一元的。我观察到的是:
我只考虑整型和指针以及这些基本类型的组合类型。我知道传递 float / double 是不同的。
std::function
尺寸是 32 字节(GCC/Linux 实现,长 + 长 + 指针 + 指针 = 32 字节。)。所以路过std::function
按值应该看起来像 pass struct Person4
在我的代码中定义。但是输出程序集显示通过 std::function
和pass很不一样struct Person3
.看起来 std::function 是通过指针传递的,对吗?为什么会有这样的差异?#include <functional>
struct Person0 {
long name;
};
long GetName(Person0 p) {
return p.name;
}
struct Person1 {
long name;
long age;
};
long GetName(Person1 p) {
return p.name;
}
struct Person2 {
long name;
long age;
long height;
};
long GetName(Person2 p) {
return p.name;
}
struct Person3 {
long name;
long age;
long height;
long weight;
};
long GetName(Person3 p) {
return p.name + sizeof(p);
}
long Invoke(std::function<long(long)> f) {
return f(20) + sizeof(f);
}
int main() {
Person3 p;
p.name = 13;
p.age = 23;
p.height = 33;
p.weight = 43;
long n = GetName(p);
std::function<long(long)> ff;
Invoke(ff);
return 0;
}
最佳答案
您要阅读的文档是 System V ABI for x86-64 ,特别是第 3.2.3 节
«参数传递»
大于 32 字节的结构始终在堆栈上。
对于 <= 32 字节的结构,有一些逻辑在进行:
合并后清理表明,如果大小大于 2 个 eighbytes(16 字节),并且第一个参数不是 SSE,或者任何其他参数都不是 SSEUP,则整个聚合被归类为 MEMORY(堆栈)。
关于std::function
的使用,最后一条规则可以解释它:
- If a C++ object has either a non-trivial copy constructor or a non-trivial destructor, it is passed by invisible reference (the object is replaced in the parameter list by a pointer that has class INTEGER)
关于在 Linux/x86-84 上传递大对象的 C++ 调用约定,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65838379/