实际上,这是一个非常有趣的问题。
首先,需要注意的是我们处于一种命令式语言中,意味着当你请求某些东西时(即使无用,例如构造一个未使用的对象),编译器需要遵守,除非它能提出等效形式。基本上,如果编译器能够证明这样做不会改变程序的含义,那么它可以省略参数。
当你编写函数调用时,最终会发生以下两件事情:
如果是内联,则不传递任何参数,这有效地意味着如果编译器能够证明所涉及的构造函数和析构函数不执行任何重要工作,则可以删除未使用的对象(甚至不需要构建)。对于标记结构,它表现良好。
当发出调用时,它会按照特定的调用约定进行发出。每个编译器都有自己的调用约定集,这些调用约定指定如何传递各种参数(this
指针等),通常试图利用可用寄存器。
由于仅使用函数的声明来确定调用约定(分离编译模型),因此实际上需要传递对象...
但是,如果我们谈论的是一个空结构,没有方法和状态,则这只是一些未初始化的内存。它不应该花费太多,但确实需要堆栈空间(至少需要保留它)。
使用llvm tryout演示:
struct tag {};
inline int useless(int i, tag) { return i; }
void use(tag);
int main() {
use(tag());
return useless(0, tag());
}
提供:
%struct.tag = type <{ i8 }>
define i32 @main() {
entry:
%0 = alloca %struct.tag, align 8
%1 = getelementptr inbounds %struct.tag* %0, i64 0, i32 0
store i8 0, i8* %1, align 8
call void @_Z3use3tag(%struct.tag* byval %0)
ret i32 0
}
declare void @_Z3use3tag(%struct.tag* byval)
注意:
- 如何删除对
useless
的调用,并且不为其构建参数
- 无法删除对
use
的调用,因此需要为临时变量分配空间(希望新版本不会将内存初始化为0)