C++
在 C++ 2011 (ISO/IEC 14882:2011) 中,相关部分似乎是:
§5.2.2 函数调用 [expr.call]
¶6一个函数可以被声明为接受更少的参数(通过声明默认参数(8.3.6))或更多的参数(通过使用省略号,...,或函数参数包(8.3.5))函数定义(8.4)中的参数。[注意:这意味着,除了使用省略号 (...) 或函数参数包的地方,每个参数都有一个参数可用。——尾注]
¶7当给定参数没有参数时,参数的传递方式使得接收函数可以通过调用 va_arg (18.10) 获取参数的值。[注意:本段不适用于传递给函数参数包的参数。函数参数包在模板实例化 (14.5.3) 期间扩展,因此在实际调用函数模板特化时,每个这样的参数都有一个对应的参数。—end note] 对参数表达式执行左值到右值 (4.1)、数组到指针 (4.2) 和函数到指针 (4.3) 标准转换。具有(可能是 cv 限定的)类型 std::nullptr_t 的参数被转换为类型 void* (4.10)。在这些转换之后,如果参数没有算术、枚举、指针、指向成员的指针或类类型,该程序格式不正确。传递具有非平凡复制构造函数、非平凡移动构造函数或非平凡析构函数的类类型(第 9 条)的潜在评估参数,没有相应的参数,由实现定义的语义有条件地支持。
如果参数具有受整数提升 (4.5) 约束的整数或枚举类型,或受浮点提升 (4.6) 约束的浮点类型,则在调用之前将参数的值转换为提升的类型. 这些提升称为默认参数提升。
我将最后两句分开以强调它们。它们是标准第 7 段的连续部分。
§4.5 整体促销 [conv.prom]
¶1 一个除bool
, char16_t
,之外的整数类型的纯右值char32_t
,或者wchar_t
其整数转换等级 (4.13) 小于 的等级,如果可以表示源类型的所有值,则int
可以转换为类型的纯右值;否则,源纯右值可以转换为类型的纯右值。int
int
unsigned
int
¶2 、 或 (3.9.1) 类型的纯char16_t
右char32_t
值wchar_t
可以转换为以下第一种类型的纯右值,该类型可以表示其基础类型的所有值:int
、unsigned int
、long int
、
unsigned long int
、long long int
或unsigned long long int
。如果该列表中的任何类型都不能表示其基础类型的所有值,则类型为char16_t
,char32_t
或的纯右wchar_t
值可以转换为其基础类型的纯右值。
等等。
C
C 有两个上下文,其中参数被默认提升。一种是函数范围内没有原型(首先由另一个答案覆盖),第二种是有省略号的原型。当然,C++ 根本不允许第一种情况。这些引用来自另一个答案选择的标准的相同部分,但这里的片段有点长。它们是通过对标准的独立分析发现的,只有在交叉检查时我才注意到这些部分是相同的。
在 C 2011 (ISO/IEC 9899:2011) 中,相关部分似乎是:
§6.5.2.2 函数调用
¶6 如果表示被调用函数的表达式具有不包含原型的类型,则对每个参数执行整数提升,并将浮点类型的参数提升为双精度。这些称为默认参数提升。如果参数的数量不等于参数的数量,则行为未定义。如果函数是使用包含原型的类型定义的,并且原型以省略号 (, ...
) 或提升后的参数类型与参数类型不兼容,则行为未定义。如果函数定义的类型不包含原型,并且提升后的参数类型与提升后的参数类型不兼容,则行为未定义,但以下情况除外:
— 一种提升类型是有符号整数类型,另一种提升类型是对应的无符号整数类型,并且值可以在两种类型中表示;
— 两种类型都是指向字符类型或 void 的限定或非限定版本的指针。
¶7 如果表示被调用函数的表达式具有包含原型的类型,则参数将隐式转换为相应参数的类型,就像通过赋值一样,将每个参数的类型视为它声明的类型。函数原型声明器中的省略号会导致参数类型转换在最后一个声明的参数之后停止。默认参数提升是在尾随参数上执行的。
“整数促销”在 §6.3.1.1 中定义:
§6.3.1 算术操作数
§6.3.1.1 布尔值、字符和整数
¶1 每个整数类型都有一个整数转换等级,定义如下:
— 任何两个有符号整数类型都不应具有相同的等级,即使它们具有相同的表示。
— 有符号整数类型的等级应大于任何精度较低的有符号整数类型的等级。
— long long int 的rank 大于long int 的rank,long int 的rank 大于int 的rank,short int 的rank 大于signed char 的rank。
— 任何无符号整数类型的等级应等于相应的有符号整数类型的等级,如果有的话。
— 任何标准整数类型的秩应大于任何具有相同宽度的扩展整数类型的秩。
— char 的等级应等于signed char 和unsigned char 的等级。
— _Bool 的等级应小于所有其他标准整数类型的等级。
— 任何枚举类型的等级应等于兼容整数类型的等级(见 6.7.2.2)。
— 任何扩展有符号整数类型相对于另一个具有相同精度的扩展有符号整数类型的等级是实现定义的,但仍受制于确定整数转换等级的其他规则。
— 对于所有整数类型 T1、T2 和 T3,如果 T1 的秩大于 T2,并且 T2 的秩大于 T3,则 T1 的秩大于 T3。
¶2 可以在表达式中使用int
或unsigned int
使用以下内容:
— 具有整数类型(除int
or之外unsigned int
)的对象或表达式,其整数转换等级小于或等于int
and
的等级unsigned int
。
_Bool
— 、int
、signed int
或类型的位域unsigned int
。
如果 anint
可以表示原始类型的所有值(受宽度限制,对于位域),则该值转换为int
; 否则,将其转换为unsigned
int
. 这些被称为整数促销。58)整数提升不会改变所有其他类型。
58)整数提升仅适用于:作为通常算术转换的一部分,适用于某些参数表达式,适用于一元 +、- 和 ~ 运算符的操作数,以及移位运算符的两个操作数,由它们各自指定子条款。
我注意到有一次,问题列出了 function void f(...);
,它是 C++ 函数而不是 C 函数;C 不允许省略号作为函数的唯一参数出现。该问题已更新为void f(int, ...);
在 C 和 C++ 中都有效的问题。