防范函数名隐藏类名的危险

时间:2015-02-15 23:34:44

标签: c++

#include <iostream>
using namespace std;

#include "other_library.h"

struct Foo
{
    Foo() { cout << "class\n"; }
};

int main()
{
    Foo();  // or Foo() in any expression
}

这会输出class,或者我们会想到。问题是,如果other_library.h碰巧有一个名为Foo的函数,其返回类型适合出现在我们使用Foo的任何表达式中,那么它会默默地改变行为,例如:

int Foo() { cout << "func\n"; return 1; }

导致func输出而main中没有任何代码更改。这很糟糕,因为有可能存在阴险和难以察觉的错误;即使other_library不是恶意,也不会发现名称冲突。


处理这个问题的好方法是什么?它最初由Dan Saks in 1997提出,而one suggested resolution所有类应该是typedef'd:

typedef struct Foo Foo;

因为编译器必须报告 typedef-name 和函数名之间的冲突。然而,这似乎并不常见 - 为什么不呢?

澄清:这个问题是关于我们的代码在没有我们注意的情况下阻止这种未检测到的行为发生变化的良好做法。 (而不是一旦我们意识到它正在发生,如何解决它,这更容易 - 例如重命名我们的课程。)

3 个答案:

答案 0 :(得分:4)

  

typedef struct Foo Foo;

     

因为编译器必须报告typedef-name和。之间的冲突   一个功能名称。然而,这似乎并不常见 -   为什么不呢?

我认为这个事实是不言而喻的:繁琐的代码很麻烦。实际上,您可以通过在自己的命名空间中工作来轻松解决此问题(假设头文件也是C ++,other_library也应该这样做。

答案 1 :(得分:3)

不幸的是,你无法避免这种情况。从C ++ 11标准(3.3.10):

  

类名(9.1)或枚举名(7.2)可以通过在同一范围内声明的变量,数据成员,函数或枚举器的名称隐藏。如果类或枚举名称和变量,数据成员,函数或枚举器在同一作用域(按任何顺序)中声明具有相同名称,则类或枚举名称将隐藏在变量,数据成员,函数或枚举器名称可见。

防范它的唯一方法是你指出的typedef技巧(多亏了!),使用命名空间,或遵守命名约定(后者在处理第三方代码时没有帮助)。您还可以尝试将include包装在命名空间中(如下所示),但正如注释所指出的,这可能会在某些情况下导致链接错误。

namespace otherlib {
#include "other_library.h"
}

然后:

otherlib::Foo();

答案 2 :(得分:1)

目前的最佳做法已经防范了这一点

  • 命名约定,例如
    void camelCase() vs class PascalCase

  • 命名空间

  • 包装器库,适用于非现代C ++的所有内容