【问题标题】:Do c++ inline friend functions cause name hiding between namespaces?c++ 内联友元函数会导致名称隐藏在命名空间之间吗?
【发布时间】:2019-10-22 11:57:02
【问题描述】:

考虑以下代码:

namespace Foo1 {
void add( int ) {}
void subtract( int ) {}
}

namespace Foo2 {


    class Bar {
    public:
        friend void add( Bar ) {}
    };

    void subtract( Bar ) {}

    void xxx() {
        int i = 0;
        using namespace Foo1;
        add( i );  // Is this an error or not?
        subtract( i ); // This is an error due to name hiding
    }
}

Foo2::xxx() 中,我使用 using 命名空间来访问Foo1::addFoo1::subtract。调用减法显然是一个错误,因为 Foo2::subtract 隐藏名称。但是Foo2::add 不应该在Foo2 中真正可见,因为它只能 可以使用 ADL 找到,它不应隐藏 Foo1::add。我的理解正确吗?

我已经在多个版本的 MSVC 和 gcc 上尝试过上述代码。前者一贯 拒绝了add(i) 电话,但我不清楚错误消息。后者一直接受它。以下哪个(如果有)是正确的?

【问题讨论】:

  • @Peter - 朋友不是班级的成员,而是他们的朋友
  • @Peter 友元函数实际上并不属于该类,即使它们是在类体内定义的。
  • @Peter 一个朋友不是该班级的成员。绝对没有Foo2::Bar::add。其中定义的addFoo2 的成员,但不会自动声明为可见的Foo2 声明,因此只能通过参数相关查找找到。

标签: c++


【解决方案1】:

我认为 GCC 就在这里。

[namespace.memdef](强调我的)

3如果非本地类中的友元声明首先声明了一个 朋友是类、函数、类模板或函数模板 最里面的封闭命名空间的成员。 朋友声明 本身不会使名称对不合格的查找可见 ([basic.lookup.unqual]) 或限定查找 ([basic.lookup.qual])。 [ 注意:朋友的名字将在其命名空间中可见,如果 在命名空间范围内提供匹配的声明(之前或 在授予友谊的类定义之后)。 — 尾注 ] 如果一个 调用友元函数或函数模板,可以找到它的名字 通过考虑来自命名空间的函数的名称查找和 与函数参数类型相关的类 ([basic.lookup.argdep])。

因此,不合格的add( i ) 本身不应找到add( Bar ) 的声明,这意味着查找应继续并考虑使用指令引入的名称。而且由于参数不是类类型,所以 ADL 是不可能的。我的结论是add( Bar ) 不应该隐藏add( int )

【讨论】:

    【解决方案2】:

    正如已经指出的那样(C++ 20 标准,9.7.1.2 命名空间成员定义)

    3 如果非本地类中的友元声明首先声明了一个类, 函数、类模板或函数模板100 朋友是 最里面的封闭命名空间的成员

    所以这个友元函数add 是命名空间Foo2 的成员。然而它在命名空间Foo2 中是不可见的,直到相应的函数声明出现在命名空间Foo2 中。并且只能通过依赖于参数的查找来找到。

    namespace Foo2 {
    
        class Bar {
        public:
            friend void add( Bar ) {}
        };
        //..
    

    如果你写在函数xxx之前,命名空间Foo1中的名称add将被隐藏

    namespace Foo2 {
    
        class Bar {
        public:
            friend void add( Bar ) {}
        };
    
        void add( Bar );
        //...
    

    也就是说,如果友元函数add 将在封闭的命名空间中重新声明。

    在函数xxx

    void xxx() {
        int i = 0;
        using namespace Foo1;
        add( i );  // Is this an error or not?
        subtract( i ); // This is an error due to name hiding
    }
    

    编译器按以下顺序考虑名称减法。首先,它查看封闭的命名空间,即命名空间Foo2。这个命名空间有声明的名字substract。因此搜索过程停止。未找到重载函数 void substract( int ),因为由于 using 指令,它被视为全局命名空间的成员。即包含 using 指令中指定的命名空间和包含 using 指令的命名空间的命名空间。

    你可以这样考虑(由于使用指令)

    // the global namespace
    
    void subtract( int ) {}
    
    namespace Foo2
    {
        class Bar {
        public:
            friend void add( Bar ) {}
        };
    
        void subtract( Bar ) {}
    
        void xxx() {
            int i = 0;
            // using namespace Foo1;
            add( i );  // Is this an error or not?
            subtract( i ); // This is an error due to name hiding
        }
    }
    

    您可以使用 using 声明代替 using 指令,使命名空间 Foo1 中的两个函数在函数 xxx 中可见。

    例如

    void xxx() {
        int i = 0;
        using Foo1::subtract, Foo1::add;
        add( i );  // Is this an error or not?
        subtract( i ); // This is an error due to name hiding
    }
    

    【讨论】:

    猜你喜欢
    • 2012-05-31
    • 1970-01-01
    • 1970-01-01
    • 2020-04-10
    • 2021-11-10
    • 2012-06-11
    • 2012-06-11
    • 2016-12-12
    • 1970-01-01
    相关资源
    最近更新 更多