为什么同时获得警告,提示已使用但未定义函数,并且未定义但未使用函数?

问题描述 投票:4回答:2

我遇到了一对看起来相互矛盾的异常的编译器警告。这是我编写的代码:

#include <functional>
#include <iostream>

namespace {
  std::function<void()> callback = [] {
    void theRealCallback(); // Forward-declare theRealCallback
    theRealCallback();      // Invoke theRealCallback
  };

  void theRealCallback() {
    std::cout << "theRealCallback was called." << std::endl;
  }
}

int main() {
  callback();
}

换句话说,我在未命名的命名空间中定义了一个lambda函数,该函数向前声明了稍后在该未命名的命名空间中定义的函数。

[当我使用g ++ 7.4.0(Ubuntu 7.4.0-1ubuntu1〜18.04.1)编译此代码时,收到以下两个警告:

CompilerWarningsNamespace.cpp:6:10: warning: ‘void {anonymous}::theRealCallback()’ used but never defined
     void theRealCallback();
          ^~~~~~~~~~~~~~~
CompilerWarningsNamespace.cpp:10:8: warning: ‘void {anonymous}::theRealCallback()’ defined but not used [-Wunused-function]
   void theRealCallback() {
        ^~~~~~~~~~~~~~~

这很奇怪,因为第一个警告说我正在使用一个函数而不定义它,而第二个警告说我正在定义一个函数而没有使用它。

运行此程序确实会产生输出

theRealCallback was called.

并且程序正常终止。

有趣的是,如果我不使用未命名的名称空间,而是给名称空间命名,则这些警告就会消失,如下所示:

#include <functional>
#include <iostream>

namespace NamedNamespace {
  std::function<void()> callback = [] {
    void theRealCallback();
    theRealCallback();
  };

  void theRealCallback() {
    std::cout << "theRealCallback was called." << std::endl;
  }
}

int main() {
  NamedNamespace::callback();
}

与原始代码一样,代码的修改版本会打印出一条消息,指出已调用[C​​0]。

有人可以解释为什么我收到这些警告吗?我的猜测是,这与lambda函数中的函数的前向声明有关,后者被解释为与未命名空间中出现的更高版本的函数不同,但如果是这种情况,我不确定我是否看到为什么这最终会联系在一起,为什么我会收到这些警告。

c++ lambda g++ compiler-warnings unnamed-namespace
2个回答
2
投票

我认为此处的theRealCallback开头与决定程序是否格式正确相关。


根据当前的标准措辞,我认为您的程序格式不正确。

基于C ++ 17标准(最终草案):

根据CWG issue 2058,您的lambda中的声明将使用外部链接

声明[basic.link]/6,因为它是块范围内的函数声明,与其他已经声明的实体不匹配。

同时根据theRealCallback[basic.link]/4.2的第二个声明具有内部链接

,因为它是未命名名称空间中函数的名称空间范围声明。

根据theRealCallback,如果程序声明同一翻译单元中具有内部和外部链接的实体,则该程序格式错误。不过,这种格式错误只是在最近才添加,作为[basic.link]/6的分辨率。


正如CWG第426期的注释中所提到的,根据CWG issue 426,即使声明具有相同的链接,它们也仅引用相同的实体,这意味着其解析中的不良形式条件不适用。

因此,如果我们严格地解释这一点,那么该程序实际上具有两个独立的功能[basic.link]/9,一个具有外部链接,一个具有内部链接。具有内部链接的一个具有定义,但是具有外部链接的一个没有定义。如果是这种情况,则程序违反了一个定义规则,因为lambda ODR中的调用void theRealCallback()使用具有外部链接的函数,该函数没有定义。这会使程序格式错误,不需要诊断。

虽然通过严格阅读标准可能是正确的解释,但我认为CWG第426号决议应在此处适用,因为上述解释永远不会适用。我不知道为什么决议中未解决上述问题。


应该解决theRealCallback();说块范围内的声明将匹配封闭名称空间的链接,然后程序将具有良好的格式,并且CWG issue 2058将具有内部链接。


[您可以看到,如果通过添加theRealCallback标志严格解释该标准,则GCC会将程序视为格式错误,这将导致该程序发出错误而不是警告。


当然,解决此问题的最简单方法是在名称空间范围的lambda外部转发声明-pedantic-errors,在这种情况下,链接肯定是内部的,任何块范围声明都会引用该声明,并采用其链接。 >


如果命名空间被命名,这不是问题,因为命名空间作用域声明也将具有外部链接。


0
投票

您不能在lambda中向前声明void theRealCallback();。通过将声明移到lambda之上,它将起作用。另外,您无需使用theRealCallback()。只需使用自动命名lambda。

std::function

这可能会使它更清晰。它具有相同的效果。

namespace {
    void theRealCallback(); // Forward-declare theRealCallback

    auto callback = [] {
        theRealCallback();      // Invoke theRealCallback
    };
    void theRealCallback() {
        std::cout << "theRealCallback was called." << std::endl;
    }
}

int main() {
    callback();
}
© www.soinside.com 2019 - 2024. All rights reserved.