12
#include <cstddef>

template<typename... Types>
constexpr std::size_t getArgCount(Types&&...) noexcept
{
    return sizeof...(Types);
}

struct A
{
    int n;

    void f()
    {
        static_assert(getArgCount(n) > 0); // not ok, why?
    }
};

int main()
{
    int n;
    static_assert(getArgCount(n) > 0); // ok
}

Why can't I get the argument count of a template function at compile-time?

error message:

1>test.cpp
1>test.cpp(17,45): error C2131:  expression did not evaluate to a constant
1>test.cpp(17,42): message :  failure was caused by a read of a variable outside its lifetime
1>test.cpp(17,42): message :  see usage of 'this'
L. F.
  • 19,445
  • 8
  • 48
  • 82
xmllmx
  • 39,765
  • 26
  • 162
  • 323

1 Answers1

10

Anything that accesses this outside constexpr context is not a constant expression, as defined in [expr.const]/2.1:

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:

  • this, except in a constexpr function or a constexpr constructor that is being evaluated as part of e;

(We need this to access n in order to pass it to getArgCount by reference)

So that's why the first case doesn't compile.

The second case compiles because it doesn't involve an lvalue-to-rvalue conversion of a non-constant (sizeof(n) does not actually "read" n).

To demonstrate this, the following will also compile:

struct A
{
    int n;

    void f()
    {
        int m = n;
        static_assert(getArgCount(m) > 0); // ok, m doesn't need `this`
    }
};

Note: Having a reference inside a constexpr context (the Types&& part) by itself doesn't break "constexpr-ness" if the lifetime of the reference began within that context: [expr.const]/2.11.2.

Another example:

struct A
{
    int n;

    void f()
    {
        static_assert(sizeof(n) > 0); // ok, don't need this for sizeof(A::n)
    }
};

The following won't compile:

    int n = 1;
    static_assert(getArgCount(n+1) > 0); // not ok, (n+1) "reads" n
Community
  • 1
  • 1
rustyx
  • 80,671
  • 25
  • 200
  • 267
  • 1
    This is probably it. This explains why `int n = 1; static_assert(getArgCount(&n));` does work (even though `&n` is not constexpr, there is no lvalue-to-rvalue conversion). – HolyBlackCat Aug 31 '19 at 11:42