Search code examples
c++templatesc++17constexprcompile-time-constant

Expression did not evaluate to a constant two level constexpr functions (compiler bug?)


I have the following code:

#include <iostream>
template<int I>
class A
{
public:
    inline constexpr static int size() { return I; }
};

template<typename T>
inline constexpr auto size(const T& arg) noexcept -> decltype(arg.size())
{
    return arg.size();
}

template<typename T>
inline constexpr void twoLevel(const T& arg) noexcept
{
    static_assert(size(arg) > 0);
}

int main()
{
    A<5> a;
    static_assert(size(a)>0); //this works
    twoLevel(a); // this does not
    return 0;
}

Which fails to compile on msvc with the error expression did not evaluate to a constant, but works with gcc. Is it gcc accepting something that's undefined behaviour? Or is it a compiler bug on msvc's part? Here's a demo: godbolt code


Solution

  • From [expr.const]/4:

    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 usable in constant expressions or
      • its lifetime began within the evaluation of e;
    • [...]

    In:

    static_assert(size(arg) > 0);
    

    We have an id-expression that refers to a variable of reference type, and the reference does not have preceding initialization, so we do not have a constant expression.

    I think this:

    static_assert(size(a) > 0);
    

    works because of "preceding initialization" - we enter constant evaluation by directly binding the reference arg to the variable a, whereas in the other case we're binding a reference to another reference.

    Both should work if you take by value though.