5

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?

Boann
  • 48,794
  • 16
  • 117
  • 146
Mikhail
  • 20,685
  • 7
  • 70
  • 146

1 Answers1

4

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.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • 1
    Damn, I love this wording "An expression e is a core constant expression unless ..." and you have 22 items. Is there any logic behind it?.. Because two situations that I mentioned just do not make sense to me :( – Mikhail Oct 20 '19 at 15:30
  • 1
    @Mikhail - It (constant expressions) started with very short white list. Then in C++11 with the introduction of `constexpr` it became a blacklist. Every published standard since sees the list grow smaller, so we get *more* constant expressions. As for this rule in particular, I suppose it's just an unfortunate circumstance with the wording. There is nothing inherently better in `value` than in the temporary the reference is bound too. Maybe the rule will be tuned and relaxed a bit in the future, who knows. – StoryTeller - Unslander Monica Oct 20 '19 at 15:46