全局命名空间朋友类无法访问命名命名空间类的私有成员

问题描述

在命名命名空间类中,我将一个类(位于全局命名空间中)声明为好友。 但是,后一类无法访问前一类的私有成员。为什么是这样?有什么办法解决吗?

Bob.h

namespace ABC {
    class Bob {
        friend class Joe;
        public:
            Bob ();
            int pub_number;
        private:
            int priv_number;
    };
}

Bob.cc

#include "Bob.h"

ABC::Bob::Bob () {
    pub_number=10;
    priv_number=6;
}

Joe.h

class Joe {
    Joe ( );
};

Joe.cc

#include "Joe.h"
#include <iostream>
#include "Bob.h"

Joe::Joe ( ) {
    ABC::Bob b;
    std::cout << b.pub_number << std::endl;
    std::cout << b.priv_number << std::endl;
}

上面的代码在编译时会产生以下错误

Joe.cc:8:16: error: ‘int ABC::Bob::priv_number’ is private within this context
INFO: 1>     8 | std::cout << b.priv_number << std::endl;

如果我执行与上述相同的代码,但没有“ Bob”类的任何名称空间,则代码将编译。

我试图按如下方式在Bob.h中转发声明Joe类:

class Joe; // This does nothing to help

class ::Joe // This produces compiler message "error: ‘Joe’ in namespace ‘::’ does not name a type"

解决方法

您需要在全局名称空间中添加一个无作用域的前向声明,并在声明好友时使用作用域运算符:

class Joe;  // Forward declaration

namespace ABC {
    class Bob {
        friend class ::Joe;  // Use the Joe class from the global scope
        public:
            Bob ();
            int pub_number;
        private:
            int priv_number;
    };
}
,

您的friend声明也需要::前缀:

class Joe;

namespace ABC {
    class Bob {
        friend class ::Joe;
        //           ^^ here

        ...
     };
 }
,

在此类定义中

namespace ABC {
    class Bob {
        friend class Joe;
        public:
            Bob ();
            int pub_number;
        private:
            int priv_number;
    };
}

朋友类Joe的声明在名称空间Joe的范围内引入了名称ABC,因为类Joe的先前声明不可见使用了不合格的名称。

根据C ++标准(10.3.1.2命名空间成员定义)

  1. ...如果朋友声明中的名称不合格也不是 template-id,声明是一个函数或一个 elaborated-type-specifier,用于确定是否为实体的查找 先前已声明的内容不得考虑以下任何范围 最内层的封闭命名空间。

您需要在全局命名空间中将类Joe的声明放在类Bob的声明之前,并且在类Bob中,您必须使用标准名称朋友班至少喜欢

class Joe;

namespace ABC {
    class Bob {
        friend class ::Joe;
        public:
            Bob ();
            int pub_number;
        private:
            int priv_number;
    };
}

或者您可以在命名空间ABC中使用using声明,例如

class Joe;

namespace ABC {
    using ::Joe;

    class Bob {
        friend class Joe;
        public:
            Bob ();
            int pub_number;
        private:
            int priv_number;
    };
}