Search code examples
c++pointerslanguage-lawyercompile-time-constantconstant-expression

MSVC accepts constant pointer to int initialized with nullptr as constant expression


After reading about constant expressions I wrote the following example that compiles with msvc but is rejected by both gcc and clang.

int *const ptr2 = nullptr;  
static_assert(!ptr2); //msvc passes but both gcc and clang rejects/fails this  

int main()
{
    
} 

Demo

As we can see the gcc rejects the code saying:

<source>:2:15: error: non-constant condition for static assertion
    2 | static_assert(!ptr2); //msvc passes but both gcc and clang rejects/fails this
      |               ^~~~~
<source>:2:15: error: the value of 'ptr2' is not usable in a constant expression
<source>:1:12: note: 'ptr2' was not declared 'constexpr'

On the other hand msvc accepts/compiles the program. I want to know which compiler is correct according to the C++ standard.


Note that I am aware that I can use constexpr kewyord and the program will compiler with all compilers then. But my question is about the behavior of the current program as per the c++ standard. Also note that I am using C++20.


Solution

  • The operand of ! is contextually converted to bool per [expr.unary.op]/9.

    Applying [conv.general]/4 this means we obtain a bool value as if by initializing a variable _ with

    bool _(ptr2);
    

    Per [dcl.init.general]/16.9 a standard conversion sequence is used to convert the initializer expression ptr2 to bool.

    In order to construct that sequence, an lvalue-to-rvalue conversion is needed, because the standard conversions to bool all require prvalues (see [conv]), but ptr2 is a lvalue expression.

    The lvalue-to-rvalue conversion is not permitted in constant expressions per [expr.const]/5.8, because the lifetime of ptr2 didn't start during the constant expression evaluation, nor is it usable in constant expressions, which is only possible for constexpr variables and const-qualified integral/enumeration type variables (see [expr.const]/4 together with [expr.const]/3)

    So MSVC is incorrect and the other compilers are correct. The initialization of ptr2 doesn't matter.

    However, if the type of the variable itself was std::nullptr_t, then [dcl.init.general]/16.8) would preempt 16.7. and the value of the bool would false without any conversion being applied. In that case, it would be a constant expression.