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

C++ 为什么int对象和函数类型之间不明确?,c++,c++11,C++,C++11,下面是代码示例: 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

下面是代码示例:

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++标准中找到(N469[Basic,LoopUp]):1 名称查找将名称的使用与该名称的一组声明相关联。通过名称查找找到的声明要么全部声明同一实体,要么全部声明函数;在后一种情况下,这些声明形成了一组重载函数


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

  • 名称的非限定查找
  • 名称的参数相关查找
  • 非限定名称查找规则,即使在查找用于函数调用的名称时,也会查找该名称的任何声明。(规则不是只搜索该名称的函数声明)。此阶段将同时查找
    ::k
    ::A::k
    ,而不管它们是函数、
    int
    s还是其他函数

    依赖于参数的查找确实有一条规则,即仅为该部分查找找到函数声明。但这与本规范无关


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

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

    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
     }
    

    这澄清了
    使用名称空间A
    实际上并不将
    A
    的成员引入
    main()
    的范围;但这意味着在全局名称空间中查找名称时(因为这是使用声明的站点的最内层封闭名称空间),也会在那里找到
    a
    中的名称。

    有三种方法来解决歧义:

    第一:

    第二:

    第三:

    由于模棱两可的性质,使用
    名称空间A
    并不值得。这就是为什么
    使用名称空间std被认为是不好的做法在全局范围内或直接在主函数中。只要不与任何其他库冲突,就可以在类/结构的函数或成员函数中使用它

    我在IDE visual studio 2017 CE中运行了此命令,以下是编译器错误:

    1>------ Build started: Project: ChemLab, Configuration: Debug Win32 ------
    1>main.cpp
    1>c:\...\visual studio 2017\projects\chemlab\chemlab\main.cpp(17): error C2872: 'k': ambiguous symbol
    1>c:\...\visual studio 2017\projects\chemlab\chemlab\main.cpp(8): note: could be 'void k(int,int)'
    1>c:\...\visual studio 2017\projects\chemlab\chemlab\main.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指令时,它将获取该名称空间中的所有内容,并使其对main可见。现在,在main中,您可以看到
    名称空间A
    全局名称空间
    。由于两者都可见,现在在名为
    k
    的查找表中有2个标识符或符号。当你调用
    k(1,1)
    时,它不知道你打算选择哪一个

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

    main.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;
    }
    
    #包括
    类字符串{
    公众:
    char*_chars;
    };
    int main(){
    使用名称空间std;
    string myString;//错误不明确您的意思是::string还是std::string?
    返回0;
    }
    
    这可能会为您提供更多的见解:


    当使用
    using指令
    时,不要认为
    变量k
    函数k
    相同范围内声明的
    。它们以前是在自己的范围内声明的。变量
    k
    位于
    ::A::k
    中,函数
    void k(int,int){}
    位于
    ::k(int,int){}
    中。在主函数中,当您使用命名空间A应用
    这里发生的事情它需要
    每个符号
    A::
    之后,它会像在
    全局
    ::
    范围
    中一样移动它以获得可见性。现在,编译器必须对可用的符号进行选择,并看到我有一个
    k
    和一个
    k
    。你的意思是
    intk(){}
    还是
    voidk(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;
    } 
    

    k(int,int)
    属于哪个名称空间?为使用
    k(1,1)
    的块指定了哪个命名空间?
    名称空间A
    是否有
    k(int,int)
    ?它是否有
    int k
    ?cppref的引用不适用,因为您没有在
    main
    @BoPersson中声明任何名称。投票结果最高的答案是“在同一范围内找到了两个名称”,因此我引用这句话来证明“在sams范围内没有两个名称”。找到了两个名称,但是它们不在同一个范围内。@bigxiao-引文说的是“正在声明”,但主要没有声明任何内容。您可以在main中声明一个新的
    k
    ,这将隐藏另外两个。这就是引用的意思。但是等等!据我所知,using指令没有在范围中引入name@毕肖为什么不呢。这就是它的目的。@bigxiao它并没有阻止您声明同一个命名变量,但您并没有这样做,而是使用了name@NathanOliver我认为“两个名字在同一个范围内”
    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;
    }