Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does a constexpr function behave differently for a reference?

Tags:

c++

constexpr

Inspired by Counting function arguments at compile time

Consider this code:

template <typename... Args>
constexpr int count(Args&&...)
{
    return sizeof...(Args);
}

void foo(int value)
{
    static_assert(count(value) >= 0);  // OK

    const int& ref = 7;
    static_assert(count(ref) >= 0);  // Error
}

First static_assert works fine. Second gives an error:

<source>:12:19: error: static_assert expression is not an integral constant expression
    static_assert(count(ref) >= 0);
                  ^~~~~~~~~~~~~~~
<source>:12:25: note: initializer of 'ref' is not a constant expression
    static_assert(count(ref) >= 0);
                        ^
<source>:11:16: note: declared here
    const int& ref = 7;
               ^

Both situations are surprising to me. Why does the first static_assert work fine, while value is clearly not known at compile time? Why does the second static_assert not work, while the only fundamental difference with the first is that it is supplied with a reference, not a value?

like image 496
Mikhail Avatar asked Oct 20 '19 11:10

Mikhail


1 Answers

As is usual with constant expressions, we need to consult the list in [expr.const] and see if any sub-expression we wrote is disallowed. In this case the pertinent bullet is this one:

2.11 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;

That's what knocks ref out of the water. It did not come into existence with the evaluation of count(ref) since it's declared beforehand, and it's not initialized with a constant expression. That may be a 7, but the actual initializer is a temporary object, since that's what the reference binds with.

As for value being usable. That is because there is no bullet that disallows value itself. And the reference argument now has its lifetime begin with the evaluation of count(value), and not beforehand. So it's a valid reference to create in a constant expression, but it just can't be used to read value.

like image 124
StoryTeller - Unslander Monica Avatar answered Nov 18 '22 05:11

StoryTeller - Unslander Monica