3

如何在 C++ 中使用以下形式的函数:void function(...) {}

真的需要至少一个隐式参数吗?

4

3 回答 3

5

从评论中重复:

在这里,C99 和 C++11 之间似乎有一个有趣的区别:C++11 允许函数声明void foo(...),因为参数声明子句中的参数声明列表是可选的:[dcl.fct]

函数声明:

D1 ( 参数声明子句 ) cv-qualifier-seq opt ref-qualifier opt异常规范opt attribute-specifier-seq opt

参数:

参数声明子句:
参数声明列表opt ...opt
参数声明列表 , ...

(注意parameter-declaration-list...是如何在此处单独选择的,这意味着您可以省略其中一个或两者。clang++ 和 g++ 支持这种解释。)

在 C99 中,不允许此声明,因为参数列表在参数类型列表中不是可选的:6.7.5/1

函数声明:

直接声明器 ( 参数类型列表 )

参数:

参数类型列表:
参数列表
参数列表 , ...

由于等宏/函数是从 C99 继承的,因此无法在 C++ 中va_start使用与省略号匹配的参数和空的参数声明列表。


C99 中的描述va_start:7.15.1.4

void va_start(va_list ap,parmN);
[...]
参数parmN是函数定义中可变参数列表中最右边参数的标识符(在 之前的那个, ...。[...]

强调我的。C99假定在省略号之前有一个参数,因为在 C99 中声明一个带有省略号但没有参数的函数是不合法的。


然而,我可以看到在 C++ 中使用带有省略号但没有任何参数的函数的两个原因:

  • 重载分辨率。将参数与省略号匹配会导致重载的排名非常低:省略号转换序列比任何用户定义的标准转换序列 [over.ics.rank]/2 都差。这对于元编程很有用:

    char foo(int);
    int  foo(...);
    
    struct S{};
    S s;
    sizeof(foo(42));    // yields 1
    sizeof(foo(s));     // yields sizeof(int)
    
  • 实现定义的技巧。您的实现可能会提供一种访问与省略号匹配的参数的方法。例如看BobTFish例子

于 2013-08-28T16:14:19.013 回答
1

如果您想保持某种平台独立性,您至少需要一个参数并使用 va_arg 宏。

如果您了解架构和调用约定的低级细节,那么您可以直接从寄存器和/或堆栈中提取参数(取决于各种参数的最终位置)。

于 2013-08-28T15:51:08.023 回答
1

鉴于va_start“函数”需要一个参数来设置va_list,我想说不可能以可靠且可移植的方式做到这一点。很可能会找到适用于特定平台的东西 - 但如果您更改编译器,为不同的平台编译,并且在某些情况下即使您使用不同的编译器选项进行编译(或更改函数中的代码,例如引入局部变量)。

... 
va_list vl;
va_start(vl, arg);
...

当然,您的另一个问题是知道什么时候没有参数(这将是一个有效的案例)。因此,如果您没有至少一个参数,那么当您根本不传递任何参数时会发生什么?你怎么“知道”这种情况?

于 2013-08-28T16:00:29.240 回答