朋友功能和命名空间

时间:2016-08-06 07:15:46

标签: c++

我试图重载<<用于属于用户定义的命名空间的类的运算符。有趣的是,如果我删除所有这些命名空间的东西,程序编译并运行没有问题,但是这个类以某种方式驻留在命名空间中的事实使得文件A.cpp的编译过程失败并出现错误说我试图访问A类中的私有数据(demo.cpp编译正常)。请看一下我的三文件程序和我得到的编译错误:

demo.cpp:

#include <iostream>
#include "A.h"


int main() {
    usr::A a(4);
    std::cout << a << std::endl;


    return 0;
}

A.H:

#ifndef A_H_
#define A_H_

#include <iostream>


namespace usr {
    class A {
        private:
            int m_x;
        public:
            A(int x);
            friend std::ostream& operator<<(std::ostream& os, const usr::A& a);
    };
}

#endif // A_H_

A.cpp:

#include "A.h"


usr::A::A(int x) : m_x(x) {}

std::ostream& operator<<(std::ostream& os, const usr::A& a) {
    os << a.m_x;
    return os;
}

错误:

$ g++ -c A.cpp
In file included from A.cpp:1:0:
A.h: In function ‘std::ostream& operator<<(std::ostream&, const usr::A&)’:
A.h:10:17: error: ‘int usr::A::m_x’ is private
             int m_x;
                 ^
A.cpp:7:13: error: within this context
     os << a.m_x;
             ^

2 个答案:

答案 0 :(得分:6)

非合格的好友声明总是引用最小的封闭命名空间的成员。在名称空间usr中声明类时,该类中的任何非限定友元声明都引用usr的成员。即你的朋友声明宣称usr::operator <<为朋友。

在这种情况下,全局::operator <<仍然是非朋友,这就是当您尝试从usr::A访问::operator <<的私人成员时收到错误的原因。

如果你想要这个,你必须

  1. 让您的operator <<成为usr的成员,或

  2. 通过使用限定名称operator <<确保好友声明明确引用全局::operator <<(这还需要在尝试之前引入::operator << 用它的合格名称来引用它。

  3. 我会建议第一种方法。如果您的类A是名称空间usr的成员,那么最好将所有处理A的函数声明为usr的成员。这将帮助您避免许多与参数相关的查找(ADL)问题。

    但是如果由于某种原因你需要你的operator <<仍然是全局命名空间的成员,那么这里是你必须跳过来使它编译的箍(组合成一个翻译单元)

    // Pre-declare `usr::A` to enable pre-declaration of our `::operator <<`
    namespace usr {
        class A;
    }
    
    // Pre-declare our `::operator <<`
    std::ostream& operator<<(std::ostream& os, const usr::A& a);
    
    namespace usr {
        class A {
            private:
                int m_x;
            public:
                A(int x);
                friend std::ostream& ::operator<<(std::ostream& os, const usr::A& a);
                // Friend declaration uses qualified name - it explicitly 
                // refers to `::operator <<` declared above
        };
    }
    
    usr::A::A(int x) : m_x(x) {}
    
    std::ostream& operator<<(std::ostream& os, const usr::A& a) {
        os << a.m_x;
        return os;
    }
    
    int main() {
        usr::A a(4);
        std::cout << a << std::endl;
    }
    

答案 1 :(得分:0)

在GCC下,你必须通过一些访问修饰符(对const或指针的引用)来分离 return-type 和范围解析运算符(:: token)。

例如,不会在g ++ 7.2.0下编译:

std::string f(int a);

namespace NS
{
    class C
    {
        friend std::string ::f(int a);
        // scope operator  ^^ is absolutely needed
    }
}

std::string f(int a);

namespace NS
{
    class C
    {
        friend std::string const ::f(int a);
        // see const keyword ^
    }
}

std::string f(int a);

namespace NS
{
    class C
    {
        friend std::string& ::f(int a);
        // see ref symbol ^
    }
}