G ++ compiles an array with a runtime size of const (not constexpr)

Can anyone clarify why this legal C ++ code? (Yes, I'm asking why my code works;))

#include <iostream>
#include <vector>

int main()
{
    const std::size_t N = 10; 

    int a[N]{}; // value-initialize it to get rid of annoying un-initialized warnings in the following line
    std::cout << a[5] << std::endl; // got a zero 
}

      

Size of the array is declared const

(the NOT constexpr

), but the program compiles without warnings ( -Wall

, -Wextra

, -Wpedantic

) in g ++, and to clang ++. I thought the C ++ standard explicitly stated that the size of the array should be a compile-time constant. This is absolutely not the case.

+3


source to share


4 answers


N4140 Β§5.19 [expr.const] / p2, bullet 2.7.1 and p3:

2 A conditional expression e

is an expression of a constant constant, the evaluation e

, following the rules of the abstract machine (1.9), will evaluate one of the following expressions:

  • [...]
  • lvalue-to-rvalue conversion (4.1) if not applied to
    • an unstable value gl of an integer or enumerated type, which refers to a non-volatile const object with prior initialization, is initialized with a constant expression [Note: string literal (2.14.5) corresponds to an array of such objects. -end note]
    • a volatile glvalue that refers to a non-volatile object defined with constexpr

      , or refers to a non-volatile sub-object of such an object, or
    • A nonvolatile glvalue of a literal type that refers to a nonvolatile object that began its lifespan as part of the evaluation e

      ;
  • [...]

3 An integral constant expression is an expression of an integer or unenumerated enumeration type implicitly converted to prvalue, where the converted expression is a constant constant expression. [Note: such expressions can be used as array bounds (8.3.4, 5.3.4) as bitfield length (9.6) as enumerator initializers if the underlying type is not fixed (7.2) and as alignment (7.6. 2) ... -end note]



In your code N

is a "non-volatile integer or enum type", it refers to a "previous-initialized non-volatile const object", so applying an lvalue-to-rval conversion does not prevent the expression from being the underlying constant expression, despite not having constexpr

.

+7


source


Where did you get the weird idea that N

"absolutely NOT a compile-time constant" as you point out in the code comments?

Since the beginning of time, a const

integral object declared with an integral constant expression initializer forms an integral constant expression by itself. That is, it is a compile-time constant in C ++.



This applies equally to namespace declarations, local declarations, and static class member declarations.

(It won't be a compile-time constant in C. But there has always been a compile-time constant in C ++.)

+6


source


Well - N is constant at compile time, so it is equivalent to

int a [10] {};

+1


source


A const int function initialized with a literal is considered a constant expression

From N1905 5.19

An integral constant expression can only include literals of arithmetic enumeration types, nonvolatile const variables, or static data members of integral or enumerated types initialized by constant expressions

Note the "non-volatile" indicating that your source code should be rejected by g ++.

+1


source







All Articles