3

(This is a follow up to this question.)

So I'd like to ask question specifically to understand the standardese quoted in that answer I received, and my exact question is in the title.

Honestly, not even on cppreference I understand what the reason is why the standard says so.

However, here's the minimal example:

#include <array>
int main() {
    auto arr = std::array<int,3>{{1,2,3}};
    constexpr auto size1 = arr.size(); // OK
    auto const& array = arr;
    constexpr auto size2 = array.size(); // does not compile
}

which does not compile with the error (the message error is the same with -std=11/14/17/2a, hence the tags for the two extremes)

$ g++ -std=c++17 deleteme.cpp && ./a.out 
deleteme.cpp: In function ‘int main()’:
deleteme.cpp:6:39: error: the value of ‘array’ is not usable in a constant expression
    6 |     constexpr auto size2 = array.size(); // does not compile
      |                                       ^
deleteme.cpp:5:17: note: ‘array’ was not initialized with a constant expression
    5 |     auto const& array = arr;
      |                 ^~~~~

but it does compile if we remove the &.

On the other, if I just relied on the note, which reads ‘array’ was not initialized with a constant expression, I would assume that the following compiles

#include <array>
int main() {
    constexpr auto arr = std::array<int,3>{{1,2,3}};
    constexpr auto size1 = arr.size(); // OK
    constexpr auto& array = arr;
    constexpr auto size2 = array.size(); // does not compile
}

but it doesn't and the compiler says (the message error is the same with -std=11/14/17/2a)

$ g++ -std=c++17 deleteme.cpp && ./a.out 
deleteme.cpp: In function ‘int main()’:
deleteme.cpp:5:29: error: ‘arr’ is not a constant expression
    5 |     constexpr auto& array = arr;
      |                             ^~~

which basically means that arr is not "a constant expression" even though it is constexpr, which looks at least a very bad wording to me.

Enlico
  • 23,259
  • 6
  • 48
  • 102
  • Could you please make up your mind about which standard document you want citations from? I edited the tags to match your reported tests, but now you added a test with C++11 – StoryTeller - Unslander Monica Dec 10 '20 at 17:54
  • 1
    @StoryTeller-UnslanderMonica described behavior is the same in all Standard versions C++11-C++20. – ecatmur Dec 10 '20 at 17:55
  • @ecatmur - Yes but no. The verbiage is increasingly more precise in every published standard. The description of the behavior is hardly the same. – StoryTeller - Unslander Monica Dec 10 '20 at 17:58
  • Related, but not going into motivation for the Standard behavior: https://stackoverflow.com/questions/51456712/why-references-cant-be-used-with-compile-time-functions https://stackoverflow.com/questions/54124899/why-is-a-constexpr-function-on-a-reference-not-constexpr?rq=1 – ecatmur Dec 10 '20 at 18:55

2 Answers2

1

I believe it's because the constant-expression rules permit us to use the address-of operator, as hinted at in this answer. For example, this is legal:

void f() {
    int i, j;
    constexpr bool b = &i == &j; // OK, b := false since i and j are distinct objects
}

It might seem harmless to permit the use of references within constant expressions:

void g() {
    int i, j;
    int& r = j;
    constexpr bool b = &i == &r; // OK, surely?
}

But then we could make the referent of a reference dependent on a non-constant variable, and so smuggle that non-constant value into the constant evaluation:

void h(bool a) {
    int i, j;
    int& r = a ? i : j;
    constexpr bool b = &i == &r; // oops, b := a
}

It should be possible to relax the prohibition on evaluating references to a prohibition on taking (or using?) the address of references, but it'd certainly require some effort to ensure the new language had the intended effect.

Addendum: The paper P2280R1 "Using unknown references in constant expressions" intends to strike the standardese that makes your original example ill-formed. It does not (yet) go into detail on exactly what will be allowed, but it seems likely that either forming or comparing pointers to "unknown" objects that are the referents of references from outside the constexpr context will be disallowed, so g() will continue to be invalid, not to mention h().

ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • Oh, I love examples! However, I have the feeling that this observation hinges on the (verifiable) fact that _addresses of variables_ are `constexpr`-ish (I guess there's a proper word/adjective, but I don't know it), in that they cannot change; `constexpr bool b = &i == &j` works just fine for instance. At the same time, however `constexpr int * b = &i;` does not compile. So it's not really about addresses being `constexpr`-ish as I thought. – Enlico Dec 10 '20 at 19:09
  • 1
    @Enlico basically, you're allowed to *use* the addresses of automatic variables within your constant-expression as long as those addresses don't "escape" - that is, become the *result* of the expression as described by http://eel.is/c++draft/expr.const#11.2 – ecatmur Dec 10 '20 at 19:28
  • 1
    That said, I could move `int i, j;` outside the above functions (into file scope) or make them `static`, and their addresses would be permitted results of a constant expression. – ecatmur Dec 10 '20 at 19:31
  • Thank you very much. This clarifies a lot! – Enlico Dec 10 '20 at 19:33
1

constexpr specifier used in a variable declaration requires the full-expression of initialization to be a constant expression, which is ruled by:

A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized. In any constexpr variable declaration, the full-expression of the initialization shall be a constant expression.

A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value satisfies the following constraints.

An entity is a permitted result of a constant expression if it is an object with static storage duration that is either not a temporary object or is a temporary object whose value satisfies the above constraints, or it is a function.

The core constant expression shall satisfy these rules:

An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:

  • [...]
  • an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either
  • it is initialized with a constant expression or.
  • its lifetime began within the evaluation of e;

In your example, the id-expression array is neither be initialized by a constant expression due to arr does not have static storage duration nor its lifetime began within the evaluation of initialization for size2 . Hence the initialization is not a constant expression, the program is ill-formed.