【问题标题】:Why does C++ code missing a formal argument name in a function definition compile without warnings?为什么在函数定义中缺少形式参数名称的 C++ 代码编译时没有警告?
【发布时间】:2017-10-06 17:16:45
【问题描述】:

在开始使用一些 VS2005 生成的 MFC 代码时,我注意到它用类似这样的东西覆盖了一个方法:

void OnDraw(CDC* /*pDC*/)
{
    ...
    // TODO: Add your code here
}

当然,一旦我添加了一些内容,我就意识到我需要取消注释 pDC 形式参数才能进行编译,但我对 C++ 函数如何/为什么可以编译感到困惑(没有警告)当形式参数只有类型而不是名称时:

void foo(int)
{
    int x = 3;
}
int main()
{
    foo(5);
    return 0;
}

这不应该至少产生一个警告(使用 -Wall 或 /W4)吗?似乎没有。我错过了什么吗?是否存在这样有用的情况,或者仅仅是因为编译器在处理该行之前无法区分函数声明(仅需要类型)和定义(完全指定)?

【问题讨论】:

    标签: c++ compiler-warnings


    【解决方案1】:

    因为有时你有一个接口需要的参数,但函数不使用它。也许该参数不再需要,仅在必须使用相同签名的其他函数中才需要(尤其是可以通过指针调用它们)或者该功能尚未实现。由于这个原因,在生成的代码或框架代码中,不使用的参数特别常见(这可能就是 MFC 生成的代码的名称被注释掉的原因)。

    至于为什么没有警告 - 我想这是因为这是否是一个问题是一个主观的事情,而其他人(尤其是编译器实现者)并不认为这是一个问题。一旦你真正开始使用参数,如果你忘记取消注释名称,编译器就会抱怨,所以只有在你真正需要它时才会让编译器抱怨(敏捷 YAGNI 的编译器版本:“You Are' t Gonna Neet It”的哲学)。

    当您启动警告时似乎通常会发生相反的情况 - 未使用的命名参数会生成警告 - 这可能也是生成的函数名称被注释掉的原因。

    【讨论】:

    • 作为一个额外的说明,许多编译器有一个编译指示来禁用未使用的参数警告 - 当然,当参数 有一个名称时更相关 - 正是因为有正当的理由为什么这样做可能发生。有些人更喜欢保留名称,以表明预期用途(根据界面),即使在这种情况下没有使用。
    • 我想这是有道理的——我认为它是这样的,它只是(在我看来)像一个不寻常的边缘条件明确允许,当(如下所述)C 不允许它。但是你关于多态调用的观点肯定是有效的,C++ 考虑过它是有道理的。
    • 关于为什么没有警告的另一个额外说明:方法签名不依赖于名称,对吧?仅在正确顺序的类型上。因此,这个名字可以完全省略。当考虑为什么完全允许使用未命名参数时,另一个没有警告的原因变得很清楚:主要是如果参数必须存在,因为该方法是接口的一部分,要求该方法具有规定的签名并且必须避免未使用的变量警告如果不使用此变量。因此,对未命名参数发出警告会适得其反。
    【解决方案2】:

    我见过的最常见的原因是抑制编译器将抛出的未使用变量警告:

    #include <iostream>
    
    void foo(int source)
    {
      std::cout << "foo()" << std::endl;
    }
    
    int main()
    {
      foo(5);
      return 0;
    }
    

    gcc 说:main.cc:3: warning: unused parameter 'source'

    有两种常见的方法可以消除警告:注释变量名或完全删除它:

    void foo(int /*source*/)
    {
      std::cout << "foo()" << std::endl;
    }
    

    void foo(int)
    {
      std::cout << "foo()" << std::endl;
    }
    

    我强烈建议评论而不是删除。否则,您的维护程序员将不得不以其他方式找出该参数代表什么。

    Qt(可能还有其他框架)提供了一个宏来抑制警告,而无需注释或删除变量名:Q_UNUSED(&lt;variable&gt;):

    void foo(int source)
    {
      Q_UNUSED(source); // Removed in version 4.2 due to locusts
      std::cout << "foo()" << std::endl;
    }
    

    这让您可以在函数体中指出未使用该变量,并为记录为什么不使用它提供了一个很好的地方。

    【讨论】:

    • 这是理性的。 (顺便说一句,C 需要参数名称)
    • @AProgrammer:问题是关于 C++,而不是 C。
    • @Bill:但它仍然是一个有价值的注释(我发现自己偶尔会被 C 对参数名称的坚持所激怒)
    • 为了记录,像 Q_UNUSED() 这样的东西通常在 GCC 上扩展为 __attribute__ ((unused))
    • @greyfade:看起来他们不再这样做了:qt.gitorious.org/qt/qt/blobs/master/src/corelib/global/…,第 1568-1574 行
    【解决方案3】:

    C++11 N3337 标准草案

    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf 表示在 8.4.1/6 是合法的 “函数定义 > 一般”:

    注意:未使用的参数无需命名。例如,

    void print(int a, int) {
        std::printf("a = %d\n",a);
    }
    

    更准确地说,8.4.1/1 表示函数定义的语法是

    function-definition:
        attribute-specifier-seqopt decl-specifier-seqopt
        declarator virt-specifier-seqopt function-body
    

    然后,如果您遵循语法定义,例如在“附件 A 语法摘要”下,您会看到名称是可选的。

    【讨论】:

      【解决方案4】:

      它编译是因为语言标准明确规定它必须编译。没有其他答案。这是使 C++ 与 C 不同的地方之一。在 C 中,函数定义中的参数名称必须存在,在 C++ 中它们是可选的。

      我真的很想知道你为什么问你的“为什么”问题。您认为这种行为有什么不自然、不寻常或不合逻辑的地方吗?

      【讨论】:

      • 正如其他答案所暗示的那样,我一直在寻找类似于“为什么标准允许这样做”的内容。 “它编译是因为它应该编译”的答案是..至少可以说是显而易见的。
      • @Andrew Coleson:嗯,对我来说,省略参数名称(虽然没有使用)看起来很自然,这是它背后的基本原理 - 显而易见。我很难理解为什么有人会问“为什么”这个问题。实际上,我更希望有人问为什么 C 不允许 允许这样做,而不是 C++ 允许这样做。所以,我上面的回答应该意味着以下内容:它在 C++ 中是合法的,因为 C++ 规范最终允许它(如“每个人都在等待它,而在 C++ 中它终于成为可能”)。
      • “每个人都希望在 C 中实现它,而在 C++ 中它终于成为可能”是对“为什么?”的有用答案,谢谢。
      • “没有其他答案”会使其他答案无效。但是:这是最没有启发性的答案。当然,它编译是因为标准对所以的规定。但是“为什么”问题的意思是,为什么标准明确允许未命名的参数。事实上,这种可能性是为了满足一些需要而迟来的。需求是什么,这就是why 完全合法的问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-11
      • 2021-09-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多