简体   繁体   中英

Friend functions and namespaces

I'm trying to overload the << operator for a class that's part of a user-defined namespace. What's interesting is that if I remove all that namespace stuff, the program compiles and runs without a problem, but the fact that the class resides in a namespace somehow makes the compilation process for the file A.cpp fail with an error saying that I'm trying to access private data in class A (demo.cpp compiles fine). Please, take a look at my three-file program and the compilation error that I get:

demo.cpp:

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


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


    return 0;
}

Ah:

#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;
}

Error:

$ 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;
             ^

Non-qualified friend declarations always refer to members of the smallest enclosing namespace. When a class is declared in namespace usr , any non-qualified friend declaration inside that class refers to members of usr . Ie your friend declaration declared usr::operator << as a friend.

The global ::operator << remains a non-friend in this case, which is why you get the error when you attempt to access private members of usr::A from ::operator << .

If you want this to work, you have to either

  1. Make your operator << a member of usr , or

  2. Make sure the friend declaration explicitly refers to global operator << by using a qualified name ::operator << (this will also require introducing ::operator << before trying to refer to it by its qualified name).

I would suggest the first approach. If your class A is a member of namespace usr , then it is a good idea to declare all functions that handle A as members of usr as well. This will help you to avoid many problems with argument-dependent lookup (ADL) down the road.


But if for some reason you need your operator << to remain a member of global namespace, then here are the hoops you'd have to jump through to make it compile (combined into a single translation unit)

// 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;
}

Under GCC you have to separate return-type and scope resolution operator ( :: token) via some access modifier (reference on const or pointer).

For example, this will not compile under g++7.2.0:

std::string f(int a);

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

But this will :

std::string f(int a);

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

And this will :

std::string f(int a);

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM