问题
我有一个遗留的代码库,类似这样:
enum MyEnum { Foo, Bar, Baz };
void someFunc(MyEnum enumVal, ...)
{
va_list args;
va_start(args, enumVal);
// do something with args...
}
我不能轻易地将MyEnum
更改为限定了作用域的枚举,因为它在我们的整个代码库中使用(其中一些我甚至没有访问权限)。编译这段代码(使用'-Wall -std=c++17'),Clang 14发出以下警告:
<source>:8:20: warning: passing an object that undergoes default argument promotion to 'va_start' has undefined behavior [-Wvarargs]
va_start(args, enumVal);
据我所知,这里的问题是C++(17,但11或14)标准中这三条规则的组合(或缺乏,见第三点):
1.无作用域枚举(在函数调用中)始终提升到其底层类型。
整数提升规则[conv.prom]
说:
1.基础类型不固定的无作用域枚举类型的纯右值(7.2)可以转换为以下类型中的第一个的纯右值,该类型可以表示枚举的所有值:[...]
1.无作用域枚举类型的纯右值,其基础类型是固定的(7。2)可以转换为其基础类型的纯右值。[...]
虽然这些规则只说明 * 可以 * 进行整数转换,但[expr.call]/7
说:
如果参数具有受整数提升[...],则参数的值在调用之前转换为提升的类型。
因此,基本上,任何enum
(不是enum class
)都必须在调用之前提升到其基础类型。
2. va_start
必须接受与结果类型“兼容”的参数
[cstdarg.syn]/1
状态下va_start
的规则:
如果参数parmN是[...]的类型与传递没有形参的实参时产生的类型不兼容,则行为未定义。
我不知道type that results when passing an argument for which there is no parameter
是什么意思。我猜这是(升职了!)类型的参数。不知道为什么这会影响parmN
参数的类型,但基本上我猜它意味着像这样的调用
someFunc(MyEnum::Foo, 42);
在这种情况下,42
就是argument for which there is no parameter
。
在任何情况下,传递给va_start
的参数的类型都是MyEnum
,参数的(提升)类型是某种整数类型。引出第三点:
3.在上下文中,“兼容”的含义不清楚
虽然C标准明确指出哪些类型是“兼容的”(参见C标准的6.2.7 Compatible type and composite type
),但C17标准在其与C的差异列表中明确指出,在[diff.basic]/6.9
中:
改变:C在几个地方允许“兼容类型”,C不允许。
因此,它没有给予出什么是“兼容类型”的规范。所以对我来说,不清楚[support.runtime]/3
中的“兼容”要求是如何解释的。如果我们应用旧的C标准的兼容性规则,我猜enum
s只能兼容enum
s,而不能兼容int
s。
如果这是一个正确的解释,我认为没有办法在va_start
中使用一个无作用域的enum
参数。
问题
在C++17中,是否有一种非未定义行为的方式将函数的未作用域enum
参数传递给va_start
?或者换个说法:给定一个可变参数函数,其最后一个命名的参数(在...
之前)是一个无作用域的enum
参数,是否有一种方法可以检索va_list
?
2条答案
按热度按时间pu82cl6c1#
这里我不确定“当传递一个没有参数的参数时产生的类型”是什么意思。
它表示
enumVal
将以省略号形式传递的类型。这是在www.example中定义的 www.example.com :当给定参数没有参数时,参数以这样的方式传递,即接收函数可以通过调用
va_arg
获得参数的值。换句话说,如果
arg
的类型是T
,则可以写va_start(args, arg)
,这样,如果arg
以省略号传递,则va_arg(args, T)
将有效。给定一个可变参数函数,它的最后一个命名参数是一个未限定作用域的枚举参数(在。..),是否有检索va_list的方法?
不。也许你可以把这个有问题的函数移到C源文件中。否则,您可以等待C++采用C23一元
va_start
。参见https://en.cppreference.com/w/c/variadic/va_startvaqhlq812#
您可以简单地更改someFunc的声明以接受
int
:现在,所有调用站点都将隐式地将enum标记转换为int(这是默认参数提升的情况),并且在调用
va_start
的函数中类型是正确的