【问题标题】:is it valid to use standard library function name as identifier in C++?在 C++ 中使用标准库函数名作为标识符是否有效?
【发布时间】:2015-11-22 15:00:09
【问题描述】:

考虑以下程序:

#include <cstdio>
int main()
{
    int printf=9;
    std::printf("%d",printf);
}

在变量声明中使用内置函数名作为标识符可以吗?这是定义明确的程序吗?我的意思是上述程序的行为是否定义明确?我很想知道 C++ 标准是否允许使用标准函数名作为变量的标识符

【问题讨论】:

  • 这样做肯定不是个好主意。
  • @πάνταῥεῖ:是的,我知道。我只是想知道它是否定义明确的行为?

标签: c++ identifier reserved-words variable-names


【解决方案1】:

它的格式正确,因为 std::printf::printf可能也已由 &lt;cstdio&gt; 声明!)声明在与您的整数相同的范围内,因此采用自动块持续时间的优先级。

[C++14: 3.3.1/1]: [..] 为了确定声明的范围,有时参考声明的潜在范围很方便。声明的范围与其潜在范围相同,除非潜在范围包含另一个同名声明。在这种情况下,内部(包含)声明区域中的声明的潜在范围被排除在外部(包含)声明区域中的声明范围之外。

例如,you generally wouldn't be able to do this at namespace scope

它的定义很好,因为标准库中的实体名称并不是固有的保留名称:

[C++14: 2.11/3]: 另外,有些标识符是为 C++ 实现和标准库 (17.6.4.3.2) 保留的,不得用于其他用途;不需要诊断。

[C++14: 17.6.4.3.2/1]: 某些名称集和函数签名始终保留给实现:

  • 每个包含双下划线 _ _ 或以下划线后跟大写字母 (2.12) 的名称都保留给实现以供任何使用。
  • 以下划线开头的每个名称都保留给实现,以用作全局命名空间中的名称。

【讨论】:

  • 还有额外的保留,例如,在 [extern.names] 中,但它们也不适用于这里。
【解决方案2】:

是的,这是定义明确的行为。您正在创建一个名为 printf 的 int,并且您的范围内当前没有任何名为 printf 的内容。在标准范围内有一个名为 printf 的东西,可能在全局范围内,但在本地范围内定义的 int printf 自动优先。

【讨论】:

  • 使用命名空间std时为什么不定义行为?本地定义肯定会覆盖外部定义吗?
  • 它不会被定义,因为他试图将 printf 作为函数调用,而编译器只会将其视为 int。
  • 我同意 Neil Kirk 的观点:只要你不声明 namespace std { int printf; } 就可以了。当然,当printfint 时调用printf 被明确定义为错误。
  • 在包含&lt;cstdio&gt; 之后可能有一个全局printf
  • @lightness 我不知道,谢谢。我赞成你的回答。
【解决方案3】:

技术上允许这样做。在全局命名空间中保留了一些名称,但在函数内部,您的变量名称无论如何都不会在函数外部可见,所以不是问题。

使用它是一个糟糕的主意。

请注意,这种方法可能存在问题。例如:

#define NULL 0

int main()
{
     int NULL = 42;
     printf("%d", NULL);
}

不允许,因为NULL 是一个宏,而不是一个范围标识符。

编辑:我要补充一点,printf 不是“内置函数”。它是一个“C 标准库函数”。 bultin 函数类似于__builtin_sin,编译器“知道”它,因此可以对其进行优化。请注意,内置函数通常使用“保留名称”,以避免始终与现有库和用户定义的名称发生冲突。

【讨论】:

    【解决方案4】:

    相对于标准库中的标识符,C++ 标准仅规定了标识符的以下重构

    3 另外,有些标识符是保留给 C++ 使用的 实现和标准库 (17.6.4.3.2) 并且不应 以其他方式使用;不需要诊断。

    和(17.6.4.3.2 全局名称)

    1 某些名称和函数签名集始终保留给 实现:

    — 每个名称都包含双下划线 _ _ 或以 下划线后跟大写字母 (2.12) 保留给 实现任何用途。

    - 每个以下划线开头的名称都保留给 在全局命名空间中用作名称的实现。

    因此您可以使用与标准函数名称一致的标识符。

    另一方面,这会使代码的读者感到困惑并导致歧义。 考虑到标准允许编译器将标准 C 函数名称放在全局命名空间中。

    【讨论】:

      【解决方案5】:

      这样做是可以的。因为你定义的变量int printf不属于std作为printf的命名空间cstdio中定义的。因此,您的程序名称实际上没有冲突。

      但是,如果你声明

      using namespace std;
      

      在你的程序之前,并且在你的程序后面没有使用std::,那么如果你不小心就会导致问题。通常,当存在名称冲突时,编译器会使用定义在最小范围内的名称。所以如果你有这样的程序:

      #include<cstdio>
      using namespace std;
      
      int main()
      {
          int printf = 42;
          printf("%d", printf);
      }
      

      编译器会返回

      error: ‘printf’ cannot be used as a function
      

      这是因为在这个程序中,printf 在函数范围内定义为 int,在全局范围内定义为函数 int printf( const char* format, ... )。由于函数作用域小于全局作用域,所以在函数int main() 中,printf 被解释为int 而不是函数。 int 不可调用,因此出现错误消息。

      【讨论】:

      • 在包含&lt;cstdio&gt; 之后,可能是一个全局printf。你不知道它只在命名空间std
      • 思路是一样的。如果名称有冲突,则使用在较小范围内定义的名称。
      • 是的,但你在开始时说,“所以你的程序名称实际上没有冲突”,这不是真的。请参阅我的回答中的现场演示链接。
      猜你喜欢
      • 2021-06-15
      • 1970-01-01
      • 2010-12-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-27
      • 1970-01-01
      • 2021-04-13
      相关资源
      最近更新 更多