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

Posted

技术标签:

【中文标题】为啥指向函数的“const”指针不能在常量表达式中使用?【英文标题】:Why are `const` pointers to functions not usable in a constant expression?为什么指向函数的“const”指针不能在常量表达式中使用? 【发布时间】:2021-02-17 15:11:37 【问题描述】:

考虑以下模板:

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

为什么尝试用 cp 不正确地实例化 f 编译器抱怨:

> 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

【问题讨论】:

“这似乎与其他实体不一致,例如整数”是倒退的。整数与其他所有内容不一致。 我不知道你把NTTP缩写成什么。 @BenVoigt 非类型模板参数。但忽略这一点,它似乎只适用于完全空的类型。 实际上,clang 在这种情况下给出了一个非常有用的诊断:“f&lt;cp&gt;() 不起作用,因为它不是具有外部链接的函数的地址”。 gcc 什么也没说 :( @user4581301 至少在那些日子里,我从没想过编译器会帮我很多。现在它们变得更好了,如果它们没有帮助,我会更加失望:p 【参考方案1】:

开始,有temp.arg.nontype#2:

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

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

然后我们可以按照 expr.const#10:

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

根据该规则,我们可以看到变量cp 不可能是一个常量表达式,因为根据expr.const#3,它甚至不是潜在常量

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

所以cp 不是非类型模板参数的有效模板参数,你会得到一个错误。

请注意,这是与ci 明显不一致的地方。由于ci 具有 const 限定的整数类型,它可以用作非类型模板参数的模板参数。

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

    g是一个有外部链接的函数,所以它的地址是一个常量表达式。

    5 是一个常数积分表达式。​​

    cexprpcexpri 都是 constexpr 变量。

【讨论】:

对非 constexpr 的潜在常数表达式的限制似乎有点过时了。自从引入了literaltype之类的东西会更有意义。 @Deduplicator 是的,没错。字面量类型在 expr.const 中使用了六次。也许它应该被认为是一个措辞缺陷?不过我不确定。

以上是关于为啥指向函数的“const”指针不能在常量表达式中使用?的主要内容,如果未能解决你的问题,请参考以下文章

C++中强行修改const常量的问题

修饰词const和指针结合

C++一天来几问

虚函数后面的const=0

C 语言const 关键字用法 ( 常量指针 - const 在 * 左边 - 修饰数据类型 - 内存不变 | 指针常量 - const 在 * 右边 - 修饰变量 - 指针不变 )

C++中常量引用指针常量指针指针常量常量引用顶层常量与底层常量