为什么我无法在编译时获取模板函数的参数计数?

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
}

为什么我无法在编译时获取模板函数的参数计数?

错误信息:

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'

constexpr 上下文之外访问 this 的任何内容都 不是 常量表达式,如 [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;

(我们需要 this 访问 n 以便通过引用将其传递给 getArgCount

这就是第一个案例无法编译的原因。

第二种情况可以编译,因为它不涉及非常数的 lvalue-to-rvalue conversionsizeof(n) 实际上并不“读取”n)。

为了证明这一点,以下也将编译:

struct A
{
    int n;

    void f()
    {
        int m = n;
        static_assert(getArgCount(m) > 0); // ok, m doesn't 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