是否在函数调用期间实际传递了一个未命名的参数?

Has*_*yed 17 c++ micro-optimization

template <typename TAG>
fn(int left, TAG, int right)
{
}

fn(0, some_type_tag(), 1);
/* or */
fn(0,int(), 1); // where the primitive, int, is not empty.
Run Code Online (Sandbox Code Playgroud)

编辑:这个问题有两个视角.

  1. 函数声明与定义.声明可能没有命名参数,但声明可能会这样做.这不是感兴趣的视角.
  2. 该模板在元编程中有所体现.有问题的参数是用于从特征中提取元结构的标记.这就是参数未命名的原因,我只关心编译时信息 - 标签的类型.

/编辑

我的标签通常是空结构,但是在我的代码的某些部分,它们是原始类型的typedef.所以,我很想知道现代编译器是否会实际传递参数.这有两个方面.

  1. 调整堆栈大小,考虑未命名参数类型的大小.
  2. 实际上用传递的值构造堆栈.

让我们把它保持到gcc 4.5和msvc 2008+

MSa*_*ers 12

C++有单独的翻译.由于参数可以在声明中命名但不能在函数定义中命名,反之亦然,因此编译器通常无法知道省略函数参数是否安全.当它全部在同一个翻译单元中时,所有内容都可以内联,参数名称与优化完全无关.

[添加]

单独的转换对于这种特定情况可能无关紧要,但是添加这种优化的编译器构建器必须关注.如果它破坏了完全有效的代码,它们就不会进行这样的优化.

对于模板,模板函数的类型必须等于非模板函数的类型,否则不可能获取其地址并将其分配给函数指针.同样,你必须考虑单独的翻译.仅仅因为你没有拿到foo<int>这个TU 的地址并不意味着你不会在另一个.


Dav*_*eas 7

参数是否已命名对函数签名没有影响,编译器应将其传入.请考虑函数声明中的未命名参数可能在定义中命名.

现在,在上述模板的特定情况下,编译器可能会内联代码,在这种情况下不会传递任何参数,并且未命名的参数将不起作用.

如果你要做的是标记以解决不同的重载,你总是可以回退到一个指针,这样即使传入它,成本也会降到最低.


Mat*_* M. 7

实际上这是一个非常有趣的问题.

首先,请注意,我们是在命令式语言,也就是说,当你问的东西(甚至是无用的,比如构建一个未使用的对象),那么编译器必须遵守,除非能拿出一个等价形式.基本上,如果可以证明这样做不会改变程序的含义,它可能会忽略参数.

当你编写函数调用时,可能会发生两件事(最后):

  • 要么是内联的
  • 或者call实际上是a

如果是内联,则没有参数传递,这实际上意味着未使用的对象可以被删除(甚至没有建)如果编译器可以证明所涉及的构造和析构不进行任何显著的工作.它适用于标签结构.

发出调用时,将使用特定的调用约定发出调用.每个编译器都有自己的一组调用约定,它们指定如何传递各种参数(this指针等),通常试图利用可用的寄存器.

由于只使用函数声明来确定调用约定(单独的编译模型),因此有必要实际传递对象...

但是,如果我们讨论的是一个空结构,没有方法也没有状态,那么这只是一些未初始化的内存.它不应该花费太多,但它确实需要堆栈空间(至少,保留它).

使用llvm试用版进行演示:

struct tag {};

inline int useless(int i, tag) { return i; }

void use(tag);

int main() {
  use(tag());
  return useless(0, tag());
}
Run Code Online (Sandbox Code Playgroud)

得到:

%struct.tag = type <{ i8 }>

define i32 @main() {
entry:
  ; allocate space on the stack for `tag`
  %0 = alloca %struct.tag, align 8                ; <%struct.tag*> [#uses=2]

  ; get %0 address
  %1 = getelementptr inbounds %struct.tag* %0, i64 0, i32 0 ; <i8*> [#uses=1]

  ; 0 initialize the space used for %0
  store i8 0, i8* %1, align 8

  ; call the use function and pass %0 by value
  call void @_Z3use3tag(%struct.tag* byval %0)
  ret i32 0
}

declare void @_Z3use3tag(%struct.tag* byval)
Run Code Online (Sandbox Code Playgroud)

注意:

  • 如何useless删除调用,并且没有为它构建参数
  • 如何调用use无法删除,因此为临时分配空间(我希望新版本不要0初始化内存)