3

Can someone clarify why is 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 
}

The size of the array is declared as const (NOT constexpr), still the program compiles with no warnings (-Wall, -Wextra, -Wpedantic) in both g++ and clang++. I thought that the C++ standard explicitly specified that the size of the array should be a compile-time constant. It is absolutely not the case here.

7
  • 3
    A const integral type can be used in a context where a constant expression is required. This feature was already in C++98. (Not sure about the combination const volatile though.) Commented Dec 21, 2014 at 1:53
  • I think a better question would be can a const volatile integral be a constant expression? Commented Dec 21, 2014 at 1:55
  • @RyanHaining I actually made a small mistake, now realized that the const volatile spits a warning in g++ actually. Re-edited the question. Just consider the const version, both g++ and clang++ compile it with no warnings. Commented Dec 21, 2014 at 1:58
  • @MattMcNabb removed the comment. I still don't get why then one cannot use const instead of constexpr e.g. in template meta - programming. From the answers I get, it looks like const behaves similarly, and that's not the case. I don't remember where exactly I've read about const, but it was explicitly mentioned that the initialization may be done at runtime. Then how can it be a compile-time constant? Is then the only difference between constexpr and const the fact that constexpr is forced to be computed (if possible) at compile-time, whereas const can be deferred to run-time? Commented Dec 21, 2014 at 3:03
  • @vsoftco: It is a compile time constant when it is initialized with a compile time constant, and the initialization happens at compile time in that case. When it is not initialized with a compile-time constant, it is not a compile-time constant. Commented Dec 21, 2014 at 3:15

4 Answers 4

7

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

2 A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (1.9), would evaluate one of the following expressions:

  • [...]
  • an lvalue-to-rvalue conversion (4.1) unless it is applied to
    • a non-volatile glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized with a constant expression [ Note: a string literal (2.14.5) corresponds to an array of such objects. —end note ]
    • a non-volatile glvalue that refers to a non-volatile object defined with constexpr, or that refers to a non-mutable sub-object of such an object, or
    • a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of e;
  • [...]

3 An integral constant expression is an expression of integral or unscoped enumeration type, implicitly converted to a prvalue, where the converted expression is a core constant expression. [ Note: Such expressions may be used as array bounds (8.3.4, 5.3.4), as bit-field lengths (9.6), as enumerator initializers if the underlying type is not fixed (7.2), and as alignments (7.6.2). —end note ]

In your code, N is a "non-volatile glvalue of integral or enumeration type", it refers to a "non-volatile const object with a preceding initialization", so applying the lvalue-to-rvalue conversion to it does not prevent the expression from being a core constant expression despite the absence of constexpr.

Sign up to request clarification or add additional context in comments.

4 Comments

I found this to be extremely confusing (even though it is the standard), and somehow making constexpr obsolete
@vsoftco How does this make constexpr obsolete? It's a special exception for const integer types (and unscoped enums). constexpr allows you to do much, much more.
obsolete is a harsh term probably, but in my opinion arr[const] shouldn't be allowed, as it is explicitly mentioned that the const is NOT a compile-time constant. I now realized that the standard considers it as a const-time expression in this case, but then my question is why use constexpr? why not just make const have the role of constexpr (whenever this is possible)?
@vsoftco : This behavior (re: const) has been around since before ISO standardization in 1998; constexpr is new to C++11. Removing this behavior now would be absolutely backwards-incompatible - how is this contentious?
6

Where did you get that strange idea that N is "absolutely NOT a compile-time constant", as you state in the code comments?

Since the beginning of times, a const integral object declared with an integral constant expression initializer by itself forms an integral constant expression. I.e. is a compile-time constant in C++.

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

(It would not be a compile-time constant in C. But it has always been a compile-time constant in C++.)

1 Comment

maybe I don't understand constexpr then, I now see absolutely no reason for using the new constexpr keyword to do the enum trick in meta-programming.
1

Well - N is constant during compilation so it is equivalent to

int a[10]{};

3 Comments

const is not the same as constexpr, a const may be initialized at runtime, and not at compile time. See the reason for the new constexpr keyword.
@vsoftco: A const which is initialized by a constant expression is a constant expression.
@BenjaminLindley That depends on the type. You can write const double d = 0;, which is initialized by a constant expression, but d may not itself be used in a constant expression.
1

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

From N1905 5.19

An integral constant-expression can involve only literals of arithmetic types enumerators, non-volatile const variables or static data members of integral or enumeration types initialized with constant expressions

Note the "non-volatile," indicating your original code should have been rejected by g++.

6 Comments

N1905? Why N1905 from 2005?
@dyp I was just looking for something prec++11 to show this isn't new behavior. T.C. seems to have found the relevant recent standard quote, so I won't bother hunting it down in other standards.
I've looked in the C++03 Standard, and I think the part about volatile is missing there. Strange (= probably a defect). Edit: Yes, here it is: open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#457
@dyp Hm, so g++ is behaving acceptably under C++03 (though unsafely) but breaking the standard in C++11.
@Ryan The document number is orthogonal to how old it is (bigger document number doesn't necessarily mean newer.) It also doesn't say anything about the state of compiler conformance, unless the documentation explicitly says that it's using that document as a basis.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.