Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

#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'
like image 232
xmllmx Avatar asked Aug 31 '19 10:08

xmllmx


1 Answers

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
like image 145
rustyx Avatar answered Nov 13 '22 22:11

rustyx