为什么 int 对象和函数类型之间不明确?

Why is it ambiguous between an int object and a function type?

本文关键字:之间 不明确 类型 函数 int 对象 为什么      更新时间:2023-10-16

下面是代码示例:

namespace A
{
int k;
}
void k(int,int){/*dosomething*/}
int main()
{
using namespace A;
k(1,1);//ooop!k is ambiguous!
}

发生了什么事?我认为它不应该模棱两可,因为它们是不同的类型。为什么模棱两可? 有了int k就不可能k(1,1)

.所以它与名字实际上是什么无关?即使不是函数类型的名称在我们使用k(1,1)时也会引起歧义,这在语法上是错误的int k因为不是函数?

查找名称k是不明确的,因为有两个匹配的声明可见,::k::A::k

确切的规则可以在C++标准 (N4659 [basic.lookup]/1) 中找到:

名称

查找将名称的使用与该名称的一组声明相关联。通过名称查找找到的声明应全部声明同一实体或全部声明函数;在后一种情况下,声明被称为形成一组重载函数。


查找用于函数调用的非限定名称有两个阶段:

  1. 名称的非限定查找
  2. 名称的参数相关查找。

非限定名称查找规则即使在查找用于函数调用的名称时,也会查找该名称的任何声明。(规则不是它只搜索该名称的函数声明)。 此阶段查找::k::A::k,无论它们是函数、ints还是其他什么。

依赖于参数的查找确实有一个规则,即只为查找的该部分找到函数声明。但这与此代码无关。


using指令的相关行为由 [basic.lookup.unqual]/2 涵盖(由我编辑以显示与此问题相关的部分):

出于非限定名称查找规则的目的,由 using-指令指定的命名空间中的声明被视为该封闭命名空间的成员。

这澄清了using namespace A;实际上并没有将A的成员引入main()的范围;但这意味着当在全局命名空间中查找名称时(因为它是 using 声明站点的最内层封闭命名空间),来自A的名称也将在那里找到。

有三种方法可以解决歧义:

第一:

int main() {
A::k = 5;
::k( 1, 1 );
}

第二:

int main() {
using namespace A;
A::k = 5;   
::k(1, 1);
}

第三:

namespace A {
int k;
}
namespace B {
void k( int, int ) { /* do something */ }
}
int main() {
using namespace A or B but not both!
if A then k = 5; okay && k(1,1); error
if B then k(1, 1); okay && k = 5; error
if both again ambiguous unless A::k = 5; || B::k(1,1);
return 0;
}

由于歧义的性质,使用using namespace A并不真正值得。这就是为什么在全局范围内或直接在主函数中进行using namespace std;被认为是不好的做法。可以在类/结构的函数或成员函数中使用它,只要不与任何其他库冲突即可。

我在我的IDE Visual Studio 2017 CE中运行了这个,这是编译器错误:

1>------ Build started: Project: ChemLab, Configuration: Debug Win32 ------
1>main.cpp
1>c:...visual studio 2017projectschemlabchemlabmain.cpp(17): error C2872: 'k': ambiguous symbol
1>c:...visual studio 2017projectschemlabchemlabmain.cpp(8): note: could be 'void k(int,int)'
1>c:...visual studio 2017projectschemlabchemlabmain.cpp(6): note: or       'int A::k'
1>Done building project "ChemLab.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

当您使用该using namespace directive时,它将获取该命名空间中的所有内容,并使其对 main 可见。所以现在在 main 中,您可以在 main 中同时看到namespace Aglobal namespace。由于您都可见,因此现在在查找表中有 2 个标识符或符号,名为k。当你打电话给k(1, 1)时,它不知道你打算选择哪一个。

这与这样做没有什么不同:

主.cpp

#include <string>
class string {
public:
char* _chars;
};
int main() {
using namespace std;
string myString; // error ambiguous did you mean ::string or std::string?
return 0;
}

这可能会为您提供更多见解:

使用using directive时,不要将variable kfunction k视为same scopedeclared。它们以前是在自己的范围内声明的。变量k::A::k中,函数void k(int,int){}::k(int,int){}中。在 main 函数中,当您应用using namespace A;这里发生的事情时,它需要every symbolafterA::,并且它会像在global::scope中一样移动它以获得可见性。现在编译器必须对可用的符号做出选择,并看到我有一个k和一个k。你是说int k(){}还是void k(int,int){}...

歧义来自名字。没有办法像函数/方法那样重载变量,因此名称中存在"冲突"。

为了获得所需的"k",您需要指定命名空间。

namespace A
{ 
int k;
}
void k(int, int) {/*dosomething*/ }
int main()
{
using namespace A;
::k(1, 1); // uses the global namespace
A::k = 5; // uses the namespace A
}

或者,将命名空间从等式中取出:

void k(int, int) {/*dosomething*/ }
void k(int, int, float) {}
int main()
{
int k;
// all of these are now ambiguous
k(1, 1);
k(1, 2, 0.4);
k = 5;
}