【问题标题】:MSVC function matching with const enum value 0与 const 枚举值 0 匹配的 MSVC 函数
【发布时间】:2016-12-09 10:24:27
【问题描述】:

我被 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 (live example) 将案例 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 的 const enum 变量,MSVC 似乎更喜欢转换为指针。我本来预计这种行为会出现在文字 0 中,但不会出现在 enum 变量中。

我的问题:MSVC 行为是否符合标准? (也许对于旧版本的 C++?)如果不是,这是一个已知的扩展或错误吗?

【问题讨论】:

  • 如何编辑您的问题并包含代码输出以保持完整性?
  • v1 明显是Code 类型,是msvc 的bug。
  • @Jarod42:问题是空指针常量匹配double* 是否比匹配用户定义的转换更好。 test 不需要 Code 所以我认为它不是那么明确。
  • GCC 和 Clang 什么?在什么标准模式下?
  • @RawN:GCC 和 Clang 在所有情况下都更喜欢转换为 MyVal。在枚举值 0 为 const (derp) 的情况下,MSVS 更倾向于将枚举值 0 视为空指针常量

标签: c++ visual-c++


【解决方案1】:

你没有说出任何标准,但让我们看看有什么区别:

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

[C++11: 5.19/3]: 文字常量表达式是纯右值核心常量表达式的文字类型,但不是指针类型。整型常量表达式是整型或无范围枚举类型的文字常量表达式。 [..]

还有:

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

[C++03: 5.19/2]: Other 表达式被视为常量表达式,仅用于非本地静态对象初始化 (3.6.2)。此类常量表达式的计算结果应为以下之一:

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

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

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

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

我相信 MSVS 正在实施 C++03 规则。

有趣的是,如果我将 GCC 置于 C++03 模式,它的行为不会改变,这在技术上是不兼容的。我怀疑语言的变化源于当时常见实现的行为,而不是相反。我可以看到一些evidence that GCC was [allegedly] non-conforming in this regard as early as 2004,所以也可能只是标准措辞的改变偶然发现了原来的 GCC 错误。

【讨论】:

  • 谢谢!后续问题:您能否将 MSVC 置于正确遵循 C++11 规则的模式? (或者当新标准改变旧行为时,他们通常如何处理......)
  • @BrunoDeFraine:据我所知,使用 MSVS,您可以获得在发布时针对最新标准实施的任何内容,并且您无法在标准之间切换。因此,如果 MSVS 在这方面正确实现了 C++11,它已经这样做了,你将无法关闭它。
  • 关于“证据”链接:4.1.2 的 GCC 接受那里指定的代码(当然不是在 C++11 模式下,gcc 4.1.2 甚至没有),所以 2004 年之后发生了什么变化?但似乎他们只将其更改为 int/bool,而不是枚举。
  • @LightnessRacesinOrbit:为什么test(4, aaa); 不显示WRONG
  • @cyberbisson:太糟糕了。
猜你喜欢
  • 2015-03-17
  • 1970-01-01
  • 1970-01-01
  • 2020-05-30
  • 1970-01-01
  • 2011-12-21
  • 1970-01-01
相关资源
最近更新 更多