c++ 是否可以将一个未限定作用域的枚举传递给va_start?

uqjltbpv  于 2023-05-02  发布在  其他
关注(0)|答案(2)|浏览(162)

问题

我有一个遗留的代码库,类似这样:

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

pu82cl6c

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_start

vaqhlq81

vaqhlq812#

您可以简单地更改someFunc的声明以接受int

void someFunc(int enumVal, ...)

现在,所有调用站点都将隐式地将enum标记转换为int(这是默认参数提升的情况),并且在调用va_start的函数中类型是正确的

相关问题