当前位置: 首页 > 知识库问答 >
问题:

MSVC函数与常量枚举值0匹配

司空俊雅
2023-03-14

我被MSVC无意中的C函数匹配咬了一口。我可以将其简化为以下测试用例:

#include <iostream>

enum Code { aaa, bbb };

struct MyVal {
    Code c;
    MyVal(Code c): c(c) { }
};

void test(int i, MyVal val) {
    std::cout << "case " << i << ": value " << val.c << std::endl;
}

void test(int i, double* f) {
    std::cout << "case " << i << ": WRONG" << std::endl;
}

const Code v1 = aaa;
      Code v2 = aaa;
const Code v3 = bbb;

int main() {
    const Code w1 = aaa;
          Code w2 = aaa;
    const Code w3 = bbb;

    test(1, v1);  // unexpected MSVC WRONG
    test(2, v2);
    test(3, v3);
    test(4, aaa);
    test(5, w1);  // unexpected MSVC WRONG
    test(6, w2);
    test(7, w3);
    return 0;
}

我希望所有7个test调用都会匹配第一个重载,而GCC(live-example)和Clang(live-example)会像预期的那样匹配:

case 1: value 0
case 2: value 0
case 3: value 1
case 4: value 0
case 5: value 0
case 6: value 0
case 7: value 1

但MSVC(实例)将案例1和5与“错误”过载匹配(我在MSVC 2013和2015中发现了这种行为):

case 1: WRONG
case 2: value 0
case 3: value 1
case 4: value 0
case 5: WRONG
case 6: value 0
case 7: value 1

对于(意外)值为0的常量枚举变量,MSVC似乎更喜欢将其转换为指针。我本以为这种行为与文字0有关,但与枚举变量无关。

我的问题是:MSVC的行为标准符合吗?(也许是旧版本的C?)如果不是,这是已知的扩展还是bug?

共有1个答案

有品
2023-03-14

您没有列出任何标准,但让我们看看它们的区别:

[C 11:4.10/1]:空指针常量是一个整数类型的整数常量表达式(5.19)prvalue,其计算结果为零或类型为std::nullptr_t。空指针常量可以转换为指针类型;结果是该类型的空指针值,并且可以与对象指针或函数指针类型的所有其他值区分开来。这种转换称为空指针转换。同一类型的两个空指针值应相等。将空指针常量转换为指向cv限定类型的指针是一次转换,而不是指针转换后的限定序列。[..]

[C 11:5.19/3]:文字常量表达式是文字类型的prvalue核心常量表达式,但不是指针类型。积分常量表达式是积分或无作用域枚举类型的文字常量表达式。[...]

以及:

[C 03:4.10/1]:空指针常量是计算结果为零的整数类型的整型常量表达式(5.19)右值。空指针常量可以转换为指针类型;结果是该类型的空指针值,并且可以与指向对象的指针或指向函数类型的指针的所有其他值区分开来。同一类型的两个空指针值应相等。将空指针常量转换为指向cv限定类型的指针是一次转换,而不是指针转换后再进行限定转换的顺序(4.4)。

[C 03:5.19/2]:其他表达式仅出于非本地静态对象初始化的目的而被视为常量表达式(3.6.2)。此类常量表达式应评估为以下之一:

  • 一个空指针值(4.10),
  • 一个空成员指针值(4.11),
  • 一个算术常数表达式,
  • 一个地址常量表达式,
  • 一个引用常量表达式,
  • 完整对象类型的地址常量表达式,加上或减去积分常量表达式,或
  • 指向成员常量表达式的指针。

这里的关键是标准语言在C 03和C 11之间发生了变化,后者引入了这种形式的空指针常量必须是文字的要求。

(它们总是需要实际上是常量并求值为0,因此您可以从测试用例中删除v2v3w2w3。)

空指针常量可以比通过用户定义的转换更容易转换为double*,因此...

我相信MSV正在执行C 03规则。

不过,有趣的是,如果我将GCC置于c03模式,它的行为不会改变,这在技术上是不符合的。我怀疑语言的变化源于当时常见实现的行为,而不是反过来。我可以看到一些证据表明,早在2004年,GCC(据称)在这方面就不符合规定,因此,也可能只是因为标准措辞发生了意外的变化,没有对GCC的缺陷进行窃听。

 类似资料:
  • 让我们看看一个需要诉诸于代码的场景,来考虑为何此时使用枚举更为合适且实用。假设我们要处理 IP 地址。目前被广泛使用的两个主要 IP 标准:IPv4(version four)和 IPv6(version six)。这是我们的程序可能会遇到的所有可能的 IP 地址类型:所以可以 枚举 出所有可能的值,这也正是此枚举名字的由来。 任何一个 IP 地址要么是 IPv4 的要么是 IPv6 的,而且不能

  • 问题内容: 我正在查看公司其他部门维护的一些Java代码,顺便说一下,这是一些前C和C ++开发人员所维护的。普遍存在的一件事是使用静态整数常量,例如 除了缺少“最终”限定符外,这种代码也让我有些不安。我本来希望看到的是,从学校开始主要接受Java的培训,这会更像 但是,论点使我失望。为什么要比后者更好呢? 问题答案: 为什么要比后者更好呢? 这样做要好得多,因为它可以为您提供类型安全性并具有自记

  • 我在服务器上创建了一个枚举,手动设置了整数值,而不是默认的从0开始递增 我的服务器正在使用aspNetCore. App 2.2.0运行。它在Startup.cs与swashuckle aspnetcore 4.0.1一起配置,以生成一个swagger json文件来描述每次服务器启动时的api。 然后,我使用NSwag Studio for windows v 13.2.3.0生成一个C sha

  • 主要内容:声明常量,实例,VB.Net打印和显示常量,声明枚举,实例常量指的是程序在执行过程中可能不会改变的固定值。 这些固定值也被称为文字。 常量可以是任何基本数据类型,如整数常量,浮点常量,字符常量或字符串文字。 也有枚举常量。 常量的处理方式与常规变量一样,只是它们的值在定义之后无法修改。 枚举是一组命名的整数常量。 声明常量 在VB.Net中,使用语句声明常量。 语句用于模块,类,结构,过程或块级别,以代替文字值。 语句的语法是: 其中, attribut

  • 问题内容: 通过阅读SCJP书籍,我在第1章“自测”中发现了类似的内容: 注意:代码编译正常。我不明白的是为什么我们可以从变量访问DOG,CAT或FISH常量。我认为(并且也写在书中)DOG,FISH,CAT是常量,其实现方式类似于。 所以,如果它们确实是静态的,为什么我们可以从中访问它们呢?最后一行是我熟悉的方式。 问题答案: 写作 和写作一样。也就是说,编译器将用其编译时类型Animal替换变

  • 问题内容: 我无法使用从常量中获取的枚举作为注释中的参数。我收到此编译错误:“注释属性[attribute]的值必须是枚举常量表达式”。 这是枚举代码的简化版本: 对于注释: 和班级: 该错误仅在方法B的“ theEnum = MYENUM_CONSTANT”中显示。字符串和int常量对于编译器是可以的,但Enum常量不是,即使它的值与methodA上的值完全相同。在我看来,这是编译器中缺少的功能