为什么在常量表达式中不能使用指向函数的const指针?

问题描述

请考虑以下模板:

using IntFnPtr = int(*)(int);
template <IntFnPtr> void f() { }

这些测试:

int g(int) { }

int main()
{
    f<&g>(); // OK

    const IntFnPtr cp = &g;
    f<cp>(); // Error -- why? 

    constexpr IntFnPtr cexprp = &g;
    f<cexprp>(); // OK
}

为什么尝试用格式错误f实例化cp编译器抱怨:

> error: no matching function for call to 'f'

live example on godbolt.org


请注意,这似乎与其他实体不一致,例如整数:

template <int> void f() { }

int main()
{
    f<5>(); // OK

    const int ci = 5;
    f<ci>(); // OK

    constexpr int cexpri = 5;
    f<cexpri>(); // OK
}

解决方法

首先,有temp.arg.nontype#2

非类型模板参数的模板参数应为模板参数类型的转换常量表达式([expr.const])。

[注1:如果模板参数是一个重载集合(或重载集合的地址,包括形成一个指向成员的指针),则从该集合中选择匹配函数([over.over])。 —尾注]

然后我们可以将其跟随到expr.const#10

类型T的转换后的常量表达式是隐式转换为类型T的表达式,其中转换后的表达式是常量表达式,并且隐式转换序列仅包含...

根据该规则,我们可以看到变量cp可能不是常量表达式,因为根据expr.const#3,它甚至不是潜在恒定的: / p>

如果变量是constexpr或具有引用或const限定的整数或枚举类型,则它可能是常量。

因此cp对于非类型模板参数不是有效的模板参数,并且会出现错误。

请注意,这就是ci出现明显不一致之处。由于ci具有const限定的整数类型,因此可以用作非类型模板参数的模板参数。

类似地,也允许对f的所有其他调用,因为每种情况下的模板参数都是一个常量表达式:

  1. g是具有外部链接的函数,因此其地址是一个常量表达式。

  2. 5是一个常数整数表达式。

  3. cexprpcexpri都是constexpr变量。