Correspondence of MSVC function with constant value enum 0

I was bitten by the inadvertent matching of C ++ functions using MSVC. I can reduce it to the following test case:

#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; } 

I expected that all 7 test calls would correspond to the first overload, and GCC ( live example ) and Clang ( live example ) correspond to this, as expected:

 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 

But the MSVC ( live example ) matches cases 1 and 5 for the β€œwrong” overload (I found this behavior in MSVC 2013 and 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 

It seems that pointer conversion is preferable to MSVC for the const enum variable with a (random) value of 0. I would expect this behavior with a literal of 0, but not with an enumeration variable.

My questions: Is MSVC behavior standard? (Perhaps for an older version of C ++?) If not, is this a known extension or bug?

+6
source share
1 answer

You do not name any standards, but look at the differences:

[C++11: 4.10/1]: The null pointer constant is an integer constant expression (5.19) prvalue of an integer type that evaluates to 0 or the value of the class std::nullptr_t . The null pointer constant can be converted to a pointer type; the result is a null value of a pointer of this type and is different from any other value of an object pointer or type of a function pointer. This conversion is called null pointer conversion. Two null pointer values ​​of the same type are compared equal. Converting a null pointer constant to a pointer to a cv-qual type is one conversion, not a sequence of pointer conversions with subsequent qualifications. [..]

[C++11: 5.19/3]: A literal constant expression is a constant value expression of the prvalue type of literal type, but not a pointer type. An integral constant expression is a literal constant expression of an integral or non-enumerated type of enumeration. [..]

and

[C++03: 4.10/1]: The null pointer constant is an integer constant expression (5.19) of the rvalue of an integer type that evaluates to zero. The null pointer constant can be converted to a pointer type; the result is a null value of a pointer of this type and is different from any other value of a pointer to an object or a pointer to a function type. Two null pointer values ​​of the same type are compared equal. Converting a constant with a null pointer to a pointer to a type with qualification cv is one conversion, not a sequence of conversion of a pointer followed by qualification conversion (4.4).

[C++03: 5.19/2]: Other expressions are considered constant expressions only for the purpose of initializing a non-local static object (3.6.2). Such constant expressions must be evaluated by one of the following:

  • null pointer value (4.10),
  • value of the zero element pointer (4.11),
  • arithmetic constant expression,
  • expression of an address constant
  • reference constant expression
  • expression of an address constant for the full type of an object plus or minus an integral constant expression or
  • Pointer to an element constant expression.

The key point here is that the standard language has changed between C ++ 03 and C ++ 11, the latter introducing the requirement that the null pointer constant of this form is a literal.

(They should always be constants and evaluated as 0, so you can remove v2 , v3 , w2 and w3 from the test file.)

The null pointer constant can be converted to double* more easily than through your custom conversion, therefore & hellip;

I believe that MSVS implements C ++ 03 rules.

Interestingly, however, if I put GCC in C ++ 03 mode, its behavior has not changed, which technically does not meet the requirements. I suspect that the change in language was due to the behavior of common implementations at that time, and not vice versa. I see some evidence that the GCC was [allegedly] inappropriate in this regard as early as 2004 , so it could also be that the standard wording changed the random ease, which was a GCC mistake.

+5
source

Source: https://habr.com/ru/post/1013029/


All Articles