#Classes and friends

57 messages · Page 1 of 1 (latest)

velvet plinth
#

How do I declare a function "friend" to a class if the class is inside a namespace and the function is in the global scope?

midnight bobcatBOT
#

When your question is answered use !solved to mark the question as resolved.

Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.

hazy garden
#

you need to have a forward declaration of the function outside of the class then

velvet plinth
#

inside the namespace?

hazy garden
#

in the correct namespace where the function is

#

i.e. the global namespace here

silver tusk
#

can you not explicitly specify the scope of friend function?

#

friend void ::foo(); like this?

hazy garden
#

yeah

#

but for that to work, there needs to be a declaration there

silver tusk
#

Hmm I see

velvet plinth
#

the friend function takes a reference to an object of the class as a parameter tho

#
error: 'string' does not name a type
 std::ostream& operator<<(std::ostream& out, const string& s);
hazy garden
#

then you also have to forward declare that 😄

velvet plinth
#

where string is the class name

hazy garden
#
namespace A {

struct Example;

}


namespace B {

void foo(const A::Example&);

}

namespace A {

struct Example {
    friend void B::foo(const Example&);

private:
    int i = 123;
};

}
velvet plinth
hazy garden
#

no, you can't even do that

velvet plinth
# hazy garden no, you can't even do that

I'm not sure why this isn't working (ignore the fact that the buffer is not null terminated)

#include <iostream>

namespace user{
    class string;
}

std::ostream& operator<<(std::ostream& out, const user::string& s);

namespace user{
    class string{
        private:
            char* m_Buffer;
            unsigned int m_size;

        public:
            string(const char* string){
                int count = 0;
                while(string[count] != '\0'){
                    count++;
                }

                m_size = count;
                m_Buffer = new char[m_size];

                for(int i = 0; i < m_size; i++){
                    m_Buffer[i] = string[i];
                }
            }

            
            friend std::ostream& operator<<(std::ostream& out, const string& s);
    };
}

std::ostream& operator<<(std::ostream& out, const string& s){
    out << s.m_Buffer;
    return out;
}

using namespace user;

int main(){
    string s = string("Hello");
    std::cout << s << std::endl; 
}
hazy garden
#

oh you want to define a steam operator?

#

that doesn't have to be done like this

#

there's no need for that to be in the global namespace

#

you can define that right in the class if you like:

struct Example {
    friend std::ostream& operator<<(std::ostream& out, const Example& e) {
        return out << e.whatever;
    }
};
velvet plinth
#

ah yea that's possible 😅 but I just wanted to learn how it can be done outside, it works if I don't use a namespace but not sure how to get it working when the class is inside one

hazy garden
#

then you put the operator definition inside the namespace as well

#

it will be found there because of ADL (argument dependent lookup)

velvet plinth
#

so you can't access functions outside a namespace from inside it?

hazy garden
#

huh?

velvet plinth
#

nvm you can

#

I don't get why it isn't working tho?

vivid chasmBOT
#
Compiler Output
<source>:35:51: error: 'string' does not name a type; did you mean 'stdin'?
   35 | std::ostream& operator<<(std::ostream& out, const string& s){
      |                                                   ^~~~~~
      |                                                   stdin
<source>: In function 'std::ostream& operator<<(std::ostream&, const int&)':
<source>:36:14: error: request for member 'm_Buffer' in 's', which is of non-class type 'const int'
   36 |     out << s.m_Buffer;
      |              ^~~~~~~~
<source>: In function 'int main()':
<source>:44:15: error: ambiguous overload for 'operator<<' (operand types are 'std::ostream' {aka 'std::basic_ostream<char>'} and 'user::string')
   44 |     std::cout << s << std::endl;
      |     ~~~~~~~~~ ^~ ~
      |          |       |
      |          |       user::string
      |          std::ostream {aka std::basic_ostream<char>}
<source>:7:15: note: candidate: 'std::ostream& operator<<(std::ostream&, const user::string&)'
    7 | std::
hazy garden
#

you have to spell it user::string outside of the namespace

#
-std::ostream& operator<<(std::ostream& out, const string& s){
+std::ostream& operator<<(std::ostream& out, const user::string& s){
    out << s.m_Buffer;
    return out;
}
velvet plinth
#
#include <iostream>

namespace user{
    class string;
}

std::ostream& operator<<(std::ostream& out, const user::string& s);

namespace user{
    class string{
    private:
        char* m_Buffer;
        unsigned int m_size;

    public:
        string(const char* string){
            int count = 0;
            while(string[count] != '\0'){
                count++;
            }

            m_size = count;
            m_Buffer = new char[m_size];

            for(int i = 0; i < m_size; i++){
                m_Buffer[i] = string[i];
            }
        }

        
        friend std::ostream& operator<<(std::ostream& out, const user::string& s);
    };
}

std::ostream& operator<<(std::ostream& out, const user::string& s){
    out << s.m_Buffer;
    return out;
}

using namespace user;

int main(){
    string s = string("Hello");
    std::cout << s << std::endl; 
}
vivid chasmBOT
#
Compiler Output
<source>: In function 'std::ostream& operator<<(std::ostream&, const user::string&)':
<source>:36:14: error: 'char* user::string::m_Buffer' is private within this context
   36 |     out << s.m_Buffer;
      |              ^~~~~~~~
<source>:12:15: note: declared private here
   12 |         char* m_Buffer;
      |               ^~~~~~~~
<source>: In function 'int main()':
<source>:44:15: error: ambiguous overload for 'operator<<' (operand types are 'std::ostream' {aka 'std::basic_ostream<char>'} and 'user::string')
   44 |     std::cout << s << std::endl;
      |     ~~~~~~~~~ ^~ ~
      |          |       |
      |          |       user::string
      |          std::ostream {aka std::basic_ostream<char>}
<source>:35:15: note: candidate: 'std::ostream& operator<<(std::ostream&, const user::string&)'
   35 | std::ostream& operator<<(std::ostream& out, const user::string& s){
      |               ^~~~~~~~
<source>:31:30: note: candidate: 'std::ostream& user::operator<<(std::ostream&,
hazy garden
#

and then like @silver tusk said you have to tell it you're talking about operator<< in the global namespace

#

because by default, a friend declaration puts/assumes the namespace the class is in

#
-friend std::ostream& operator<<(std::ostream& out, const string& s);
+friend std::ostream& ::operator<<(std::ostream& out, const string& s);
velvet plinth
#

Ah, this works, thank you

#

but the global scope resolution :: should only be put in the declaration inside the class?

#

putting it outside as well gives me:

error: explicit qualification in declaration of 'std::ostream& operator<<(std::ostream&, const user::string&)'
 std::ostream& ::operator<<(std::ostream& out, const user::string& s);

why is that?

#

it seems like it should be a warning?

silver tusk
#

You dont have to put it on the declaration outside, since the scope in which it is being declared is already taken into account.

#

If the declaration is present in global scope then it means that the function is present in global scope, and it shall be defined there.

velvet plinth
#

why does the compiler give an error tho? it doesn't like much of a deal

hazy garden
#

a friend declaration is a bit special

#

but other declarations just... cannot be qualified, in general

#

you can't be in namespace A and declare something in namespace B

#

(well except specializations :vv )

velvet plinth
hazy garden
#

why would C++ add rules to allow this special case of a qualified declaration that has no effect 😄

velvet plinth
#

yea makes sense, thank you & @silver tusk for you time and help

midnight bobcatBOT
#

Thank you and let us know if you have any more questions!

This thread is now set to auto-hide after an hour of inactivity

velvet plinth
#

!solved