【问题标题】:Why the Compiler does not detect correct function signature in error?为什么编译器错误地检测不到正确的函数签名?
【发布时间】:2011-08-18 17:52:24
【问题描述】:

这个问题是针对 this 的,该问题是不久前发布的。
虽然 OP 很高兴接受解决了他的问题的答案,但我对编译器为何给出看似错误的错误的细节有点感兴趣。

下面是我创建的一个小代码示例来演示相同的内容:

    class YourClass
    {
    };

    class YourClass2
    {
    };
    class MyClass
    {
        public:
            void doSomething(YourClass2 obj)
            {
                //Nothing more Interesting to do
            }

    };

    int main()
    {
        YourClass *ptr = new YourClass();
        MyClass obj;
        obj.doSomething(ptr);

        return 0;
    }

GCC(4.3.4) 编译 this 会得到一个看似奇怪的错误结果:

prog.cpp: In function ‘int main()’:
prog.cpp:23: error: no matching function for call to ‘MyClass::doSomething(YourClass*&)’
prog.cpp:13: note: candidates are: void MyClass::doSomething(YourClass2)

所以问题是:
为什么编译器会处理调用,

obj.doSomething(ptr);

作为对带有原型的函数的调用,

MyClass::doSomething(YourClass*&)

而不是

MyClass::doSomething(YourClass*)

这似乎是显而易见的情况。

【问题讨论】:

    标签: c++ function gcc


    【解决方案1】:

    首先,请注意表达式 ptr(不是变量 ptr)的类型为YourClass *&。这个很重要;这是引用类型起作用的唯一方式(否则当您执行YourClass *&x = ptr 时,您将引用copy,这也是YourClass *&x = (ptr + 1) 失败的原因)。因此,编译器开始搜索带有MyClass::doSomething(YourClass *&) 的函数。

    当然,这个调用可以匹配原型MyClass::doSomething(YourClass *)。它还可以匹配MyClass::doSomething(const YourClass *) 或许多其他人。可能有几十个(或者,有多个参数,很容易成百上千个)原型可以匹配这个调用;但是,一个都找不到。

    所以编译器放弃了,并给出了一个错误。在错误中,它列出了每一个理论上可能的匹配,而是引用了一个最接近它最初寻找的原型;即MyClass::doSomething(YourClass *&)

    【讨论】:

    • 第一个签名是最通用的怎么办?我认为这是具有隐式转换的那个,而不是第二个。
    • @bdonlan:好吧,这引出了另一个问题:为什么编译器会这样搜索?标准要求那样吗?
    • 虽然您可以进行从 Foo 到 Foo& 的转换,但这意味着即使可以进行引用,也会强制进行复制。
    • @Nawaz,我手头没有标准的副本,但基本上,它必须尝试值可能衰减的各种注释组合(常量、易失性、引用等)至。列出所有可能性会使 C++ 错误(已经很冗长)太长了。
    • @Als,被调用的函数没有签名(YourClass *)。您传入了对指针的引用(即,表达式 ptr 的类型为YourClass *&)。如果它只能找到一个原始指针(YourClass *),它会将该引用指针转换为指针并传递它。
    【解决方案2】:

    正如其他人所说,编译器试图提供帮助,但可能会让您感到困惑。让我们从最简单的错误开始:

    错误:没有匹配函数调用obj.doSomething(ptr)

    虽然错误消息是正确的,但它提供的信息非常有限。在这个例子中,代码很容易理解,但考虑一段更复杂的代码。您阅读了错误消息,您可能会想... 什么是 obj?,什么是 ptr? 所以它会尽力帮助您并告诉您 obj 是什么:

    错误:没有匹配的函数调用'MyClass::doSomething(ptr)'

    好吧,那更好,它至少告诉您需要在哪个类中查找重载,但考虑到该类是 std::ostream 和函数 operator<<... 重载太多了,还有,ptr 的类型是什么?所以它继续前进并尝试描述参数:参数是YourClass*...类型的lvalue,我已经看到过去产生的那种类型的错误消息:

    错误:对于以 YourClass* 左值作为参数的“MyClass::doSomething”的调用没有匹配的函数。

    好的,错误报告就完成了。现在认为该函数可能有更多参数,错误消息可能会变成一个复杂的野兽(想象一个包含 5 个“XXX 类型的右值和 YYY 类型的左值和...”的列表)。接下来是使错误消息的语法同样精确(参数的 lvalue-ness 很重要,或者 rvalue-ness 很重要,因此一条信息必须存在)。所以它再次重写了错误信息:

    错误:没有匹配的函数调用“MyClass::doSomething(YourClass*&)”

    问题在于您试图将其解释为函数签名,但它是函数调用的描述。

    错误消息在标准中没有定义,它们在不同的编译器之间存在差异,甚至在同一编译器中从一个版本到另一个版本也是如此。归根结底,您需要学习阅读错误消息及其含义。

    【讨论】:

    • +1。我喜欢对编译器消息的逐步分析。 :-)。而最重要的一点是The problem is that you are trying to interpret that as a function signature, but it is rather the description of the function call.
    【解决方案3】:

    编译器试图提供帮助。

    MyClass::doSomething(YourClass*&)
    

    这没有命名任何函数:您的代码中没有与此匹配的函数。

    YourClass*& 是您尝试传递给名为 MyClass::doSomething 的函数的参数类型:参数 (ptr) 是一个左值,在错误中由 & 表示。

    编译器需要一些方法来区分左值参数和右值参数,以便为您提供尽可能多的有用诊断信息;这是一种简洁的方法。

    【讨论】:

      【解决方案4】:

      编译器不需要生成对您来说应该有意义的消息。它需要告诉您代码中存在错误的所有内容。现在它打印什么消息取决于编译器。该标准没有说明它应该打印什么信息,以及不应该打印什么信息。在我看来,当编译器打印错误消息时,该错误消息可能是好的/有帮助的,也可能是坏的/无用的,但不是“错误”。

      【讨论】:

      • 我知道,问题是,编译器不需要特别努力来检测该函数签名,但它会给出不匹配的诊断消息,问题是为什么?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-11-16
      • 1970-01-01
      • 2012-08-17
      • 2017-12-07
      • 1970-01-01
      • 2016-04-30
      • 1970-01-01
      相关资源
      最近更新 更多