Почему я не могу получить количество аргументов шаблонной функции во время компиляции?

#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
}

Почему я не могу получить количество аргументов шаблонной функции во время компиляции?

сообщение об ошибке:

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'

Ответ 1

Все, что обращается к this вне контекста constexpr, не является not константным выражением, как определено в [expr.const]/2.1:

Выражение e является основным константным выражением , если при вычислении e, следуя правилам абстрактной машины, не будет выполнено одно из следующих выражений:

  • this, за исключением функции constexpr или конструктора constexpr, который оценивается как часть e;

(Нам нужен this для доступа к n, чтобы передать его в getArgCount по ссылке)

Так вот почему первый случай не компилируется.

Второй случай компилируется, потому что он не включает преобразование lvalue-в-значение непостоянной величины (sizeof(n) на самом деле не "читает" n).

Чтобы продемонстрировать это, также будет скомпилировано следующее:

struct A
{
    int n;

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

Примечание. Наличие ссылки внутри контекста constexpr (части Types&&) само по себе не нарушает "constexpr-ness", если время жизни ссылки началось в этом контексте: [expr.const]/2.11.2.

Другой пример:

struct A
{
    int n;

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

Следующее не будет компилироваться:

    int n = 1;
    static_assert(getArgCount(n+1) > 0); // not ok, (n+1) "reads" n