静态函数重载的调用不明确

12

我对这种情况感到困惑,通过谷歌搜索也没有找到答案。基本上,我有以下简单代码,但它无法编译:

#include <iostream>

class A
{
public:
    int a(int c = 0) { return 1; }
    static int a() { return 2; }
};

int main()
{
    std::cout << A::a() << std::endl;
    return 0;
}

在编译这段代码时,GCC 4.2 报告说在 main() 中调用 A::a() 存在二义性,因为存在两个版本的 a() 都是合法的候选函数。而苹果公司的 LLVM 编译器 3.0 则能够正常编译。

为什么 GCC 对我想要调用的函数感到困惑?我认为通过使用 A:: 限定符来表示我要调用的是静态版本的函数是很明显的。如果我删除了 static 函数 a(),那么这段代码自然仍无法编译,因为使用 A::a() 来调用非静态的 a() 不是有效的语法。

感谢任何评论!


顺便说一句,我快速通过谷歌搜索了一下:“C++中静态和非静态成员函数的名称解析”,发现了另一个SO的问题:https://dev59.com/LG435IYBdhLWcg3whQg5 - Bingo
哎呀,我的谷歌功夫失败了... - fang
2个回答

6
这是因为C++规定这种情况是不明确的。重载决议规定在A::a中,由于this不在作用域内,该调用的参数列表将被一个虚构的A对象参数增强,而不是*this。重载决议不会排除非静态成员函数,而是

如果参数列表被虚构对象增强,并且重载决议选择T的一个非静态成员函数,则该调用是不合法的。

这最近在委员会中进行了广泛讨论,涉及core issue 1005。请参见core issue 364,其中考虑了更改此规则,但未这样做。

谢谢您提供的参考。然而,我仍然不理解为什么“重载决议会选择T的非静态成员函数之一”。如果我理解您的话正确的话,非静态a()有两个参数:this和带默认值的参数(c = 0)。静态a()有一个“人为构造的A对象参数”。因为我在调用中没有提供“this”指针,为什么会选择非静态版本? - fang
不,this在此调用中不是参数,因为范围内没有this。这就是我们在调用中传递“虚构的A对象参数”的原因。这两个函数都有一个隐式对象参数(对于静态函数,该参数仅用于接收虚构的A对象参数)。非静态a的隐式对象参数在您的情况下具有类型A&(在const成员fncs中为A const&)。调用A :: a()中的虚构对象参数匹配两个隐式对象参数,因此存在歧义。 - Johannes Schaub - litb
那么这个“人为的A对象参数”和*this是相同类型的吗? - fang
@fang,请查看规范中的13.3.1.1.1p3和13.3.1。 - Johannes Schaub - litb

3
原因是名称解析发生在编译器进行的任何其他操作之前,例如确定使用哪个重载函数。
通过使用“A::”来限定函数,只是告诉编译器“在A中查找名称为a”。它实际上并没有帮助解决你所指的函数是哪一个。
编辑
因此,当您键入A::a()时,首先编译器会想:“在A中查找可以使用operator()的成员函数或成员。”
然后编译器会想,“好的,这里有两种可能性,到底是指哪一个?a()还是带有默认值c=0a(int c = 0)。不确定。”
如果您删除了static关键字并像obj.a()一样调用这些函数,则仍然存在歧义。
关于LLVM解析器
我认为它为您做了一些额外的工作,这并不是标准所要求的,即假设A::a()是静态的。

我不知道。但是名称混淆是链接器的问题,而不是编译器的问题。 - Oliver Charlesworth
@fang 你使用的是哪个编译器? - Bingo
@Bingo i686-apple-darwin11-llvm-g++-4.2 是抱怨歧义的那个。我尝试了 g++-mp-4.7,但是出现了相同的错误。 - fang
@fang 请看我的编辑。编译器无法从参数中确定是哪一个,因为您可能指的是默认情况下的 int c = 0 - Bingo
是的,我明白你的观点。我猜那一定是原因。然而,我仍然对gcc感到不满,因为LLVM编译器似乎能够理解我想做什么。在Xcode中编写了大量代码后,突然决定尝试使用gcc进行构建,看到这样的编译错误真是令人沮丧,特别是当我一直小心地避免可能是编译器特定的棘手设计时。 - fang
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接