18

在一个相关问题中,据说没有指向非成员 const 函数的指针。另外,C++11 8.3.5/6 说

函数声明器中 cv-qualifier-seq 的效果与在函数类型之上添加 cv-qualification 不同。在后一种情况下,将忽略 cv 限定符。[ 注意:具有 cv-qualifier-seq 的函数类型不是 cv-qualified 类型;没有 cv 限定的函数类型。——尾注]

如果我理解正确,这意味着不存在非成员 const 函数。(虽然这些函数不是 const 的,但它们不能按照 3.10/6 进行修改)。特别是,指向 const 函数的指针毫无意义。

但是,似乎某些编译器确实在类型推导上下文中创建了指向 const 函数的指针。例如,考虑以下代码:

#include <iostream>

void f() {}

template <typename T> void g(      T*) { std::cout << "non const" << std::endl; }
template <typename T> void g(const T*) { std::cout << "const    " << std::endl; }

int main() {
     g(f);
}

当使用 GCC 和 Intel 编译时,代码输出“non const”,正如我从上面的引用中所期望的那样。但是,使用 Clang 和 Visual Studio 编译时,输出为“const”。

我的解释正确吗?

更新:

在评论之后,我试图澄清我不是在谈论 const 成员函数。我对非成员函数感兴趣(但同样的论点可能也适用于非静态成员函数)。我还更改了问题标题以使其更准确。

与上述解决方案一致g(f),以下行对于 GCC 和 Intel 是非法的,但对于 Clang 和 Visual Studio 是不合法的

const auto* ptr = &f;

更新 2:

我同意 Andy Prowl 的解释并选择了他的答案。但是,在那之后,我意识到这个问题是 CWG 的未决问题

4

1 回答 1

10

如果我理解正确,这意味着不存在非成员 const 函数。(虽然这些函数不是 const 的,但它们不能按照 3.10/6 进行修改)。特别是,指向 const 函数的指针毫无意义。

是的,没有函数这样的东西,并且由于您引用的段落相同,因此忽略const了创建函数的尝试。这很重要,因为无论如何创建函数类型的程序不是格式错误的(就像 C++03 中的情况一样);简单地说,它的尝试被忽略,而是考虑非函数类型。constconst

这可能是 GCC 和 ICC 无法应用的原因,因为当非const重载被移除时,程序无法编译

#include <iostream>

void f() {}

template <typename T> void g( T const*) 
{ 
   std::cout << "const    " << std::endl; 
}

int main() {
     g(f); // ERROR with GCC and ICC, compiles with Clang
}

关于你的解释:

当使用 GCC 和 Intel 编译时,代码输出“non const”,正如我从上面的引用中所期望的那样。但是,使用 Clang 和 Visual Studio 编译时,输出为“const”。我的解释正确吗?

我不相信。据我所知,Clang 是对的。

这个结论是基于两个函数模板都是可行的,因为const限定符在函数类型上被忽略,并且一个比另一个更专业。

实际上,根据 C++11 标准的第 8.3.5/7 段:

[...] cv-qualifier-seq 在函数声明器中的效果与在函数类型之上添加 cv-qualification 不同。在后一种情况下,cv 限定符被忽略。[...]

这有效地使第二个函数模板可用于解决调用(显然是第一个)。但由于这两个函数模板都是可行的,关于重载决议的第 13.3.3.1 段开始发挥作用:

鉴于这些定义,如果对于所有参数 i,ICSi(F1) 不是比 ICSi(F2) 更差的转换序列,则可行函数 F1 被定义为比另一个可行函数 F2 更好的函数,然后

— 对于某些参数 j,ICSj(F1) 是比 ICSj(F2) 更好的转换序列,或者,如果不是,

— 上下文是通过用户定义的转换(见 8.5、13.3.1.5 和 13.3.1.6)和从 F1 的返回类型到目标类型(即被初始化的实体的类型)的标准转换序列进行的初始化是比从 F2 的返回类型到目标类型的标准转换序列更好的转换序列。[ ... ] 或者,如果不是这样,

— F1 是非模板函数,F2 是函数模板特化,或者,如果不是,

F1 和 F2 是函数模板特化,根据 14.5.6.2 中描述的偏序规则,F1 的函数模板比​​ F2 的模板更特化。

由于第二个函数模板比​​第一个更专业,因此应通过重载决议选择第二个函数模板。因此,Clang 是对的

于 2013-03-22T21:22:07.667 回答