Trouble with overloading << operator via friend

M

magnus.moraberg

Hi, I have the following class -

#ifndef CASE_H_
#define CASE_H_

#include <QString>
#include <QStringList>
#include <QRegExp>
#include <QDir>
#include <stdexcept>
#include <iostream>

namespace FS
{

class Case
{
private:
QString name;
unsigned int version;

public:

...

friend std::eek:stream& operator<<(std::eek:stream& outStream, const Case&
case1);
};

}
#endif //CASE_H_

In my cpp file I include this function -

#include "Case.h"

using namespace FS;

....

std::eek:stream& operator<<(std::eek:stream& outStream, const Case& case1)
{
return outStream << case1.name.toStdString() << " v" <<
case1.version.toStdString();
}

However, this gives the error -

Case.h: In function ‘std::eek:stream& operator<<(std::eek:stream&, const
FS::Case&)’:
Case.h:27: error: ‘QString FS::Case::name’ is private
Case.cpp:50: error: within this context
Case.h:28: error: ‘unsigned int FS::Case::version’ is private
Case.cpp:50: error: within this context
Case.cpp:50: error: request for member ‘toStdString’ in ‘case1-
FS::Case::version’, which is of non-class type ‘const unsigned int’

Does the friend not have access to the private members?

Thanks,

Barry.
 
M

magnus.moraberg

Hi, I have the following class -

#ifndef CASE_H_
#define CASE_H_

#include <QString>
#include <QStringList>
#include <QRegExp>
#include <QDir>
#include <stdexcept>
#include <iostream>

namespace FS
{

class Case
{
private:
        QString name;
        unsigned int version;

public:

        ...

        friend std::eek:stream& operator<<(std::eek:stream& outStream, const Case&
case1);

};
}

#endif //CASE_H_

In my cpp file I include this function -

#include "Case.h"

using namespace FS;

...

std::eek:stream& operator<<(std::eek:stream& outStream, const Case& case1)
{
        return outStream << case1.name.toStdString() << " v" <<
case1.version.toStdString();

}

However, this gives the error -

Case.h: In function ‘std::eek:stream& operator<<(std::eek:stream&, const
FS::Case&)’:
Case.h:27: error: ‘QString FS::Case::name’ is private
Case.cpp:50: error: within this context
Case.h:28: error: ‘unsigned int FS::Case::version’ is private
Case.cpp:50: error: within this context
Case.cpp:50: error: request for member ‘toStdString’ in ‘case1-


Does the friend not have access to the private members?

Thanks,

Barry.

Hi,

I tried -

std::eek:stream& FS::eek:perator<<(std::eek:stream& outStream, const Case&
case1)
{
return outStream << case1.name.toStdString() << " v" <<
case1.version;
}

but this gave me -

Case.cpp:49: error: ‘std::eek:stream& FS::eek:perator<<(std::eek:stream&, const
FS::Case&)’ should have been declared inside ‘FS’


Then I tried this -

#include "Case.h"

using namespace FS;

....

namespace FS
{
std::eek:stream& FS::eek:perator<<(std::eek:stream& outStream, const Case&
case1fg
{
return outStream << case1.name.toStdString() << " v" <<
case1.version;
}
}

and now it works. Not sure I fully understand though. Pity book
examples don't include namespaces in all examples...
 
N

Neelesh

It has, but operator<< is defined inside namespace FS and hence it
must be defined by explicitly qualifying with FS:: or inside the
namespace FS. Otherwise the compiler will think that you are defining
a different operator<< in the global namespace.
I tried -

std::eek:stream& FS::eek:perator<<(std::eek:stream& outStream, const Case&
case1)
{
        return outStream << case1.name.toStdString() << " v" <<
case1.version;

}

but this gave me -

Case.cpp:49: error: ‘std::eek:stream& FS::eek:perator<<(std::eek:stream&, const
FS::Case&)’ should have been declared inside ‘FS’

Well, the C++ standard seems to say that explicit qualification as
done above is fine:
7.3.1.2/2:

Members of a named namespace can also be defined outside that
namespace by explicit qualification
(3.4.3.2) of the name being defined, provided that the entity being
defined was already declared in the namespace and the definition
appears after the point of declaration in a namespace that encloses
the declaration’s namespace.

Also, 7.3.1.2/3:
If a friend declaration in a nonlocal class first declares a class or
function83) the friend class or function is a member of the innermost
enclosing namespace

This means that operator<< belongs to namespace FS and it can be
defined outside namespace FS by explicitly qualifying the name, like
FS::eek:perator<<

Which compiler did you try on? g++ 3.4 seems to compile this code
correctly, and I could'nt try on latest version. Comeau online,
however gives error. I'm not sure why.
Then I tried this -

#include "Case.h"

using namespace FS;

...

namespace FS
{
std::eek:stream& FS::eek:perator<<(std::eek:stream& outStream, const Case&
case1fg
{
        return outStream << case1.name.toStdString() << " v" <<
case1.version;

}
}

and now it works.

Yes it will, since we are explicitly defining operator<< inside
namespace FS. Further, there is no need of explicitly qualifying
operator<< now, you can simply say

namespace FS
{
std::eek:stream& operator<<(std::eek:stream& outStream, const Case& case1)
}
 
N

Neelesh

It has, but operator<< is defined inside namespace FS and hence it
must be defined by explicitly qualifying with FS:: or inside the
namespace FS. Otherwise the compiler will think that you are defining
a different operator<< in the global namespace.








Well, the C++ standard seems to say that explicit qualification as
done above is fine:
7.3.1.2/2:

Members of a named namespace can also be defined outside that
namespace by explicit qualification
(3.4.3.2) of the name being defined, provided that the entity being
defined was already declared in the namespace and the definition
appears after the point of declaration in a namespace that encloses
the declaration’s namespace.

Also, 7.3.1.2/3:
If a friend declaration in a nonlocal class first declares a class or
function83) the friend class or function is a member of the innermost
enclosing namespace

This means that operator<< belongs to namespace FS and it can be
defined outside namespace FS by explicitly qualifying the name, like
FS::eek:perator<<

Which compiler did you try on? g++ 3.4 seems to compile this code
correctly, and I could'nt try on latest version. Comeau online,
however gives error. I'm not sure why.

hmm, g++ 4.3 seems to give the error that you pointed out.

7.3.1.2/3 says:

If a friend declaration in a nonlocal class first declares a class or
function83) the friend class or function is a member of the innermost
enclosing namespace. The name of the friend is not found by simple
name lookup until a matching declaration is provided in that namespace
scope (either before or after the class declaration granting
friendship).

The second part of above paragraph indicates that the name of the
friend would not be found until a matching declaration is provided
into the namespace scope. This explains why the above error comes: You
cannot use FS::eek:perator<< until operator<< is declared inside
namespace FS.

Observe that the above rule doesn't need "definition" to be inside
namespace FS. Hence the following would compile well:

namespace FS
{
std::eek:stream& operator<<(std::eek:stream& outStream, const Case&
case1) ;
}
std::eek:stream& FS::eek:perator<<(std::eek:stream& outStream, const Case&
case1)
{
        return outStream << case1.name.toStdString() << " v" <<
case1.version;
}

Once we declare the name inside namespace FS, we can define it using
FS::eek:perator<< in the global namespacea also.

Hope this helps.
 
J

James Kanze

Hi, I have the following class -
#ifndef CASE_H_
#define CASE_H_
#include <QString>
#include <QStringList>
#include <QRegExp>
#include <QDir>
#include <stdexcept>
#include <iostream>
namespace FS
{
class Case
{
private:
QString name;
unsigned int version;


friend std::eek:stream& operator<<(std::eek:stream& outStream, const Case&
case1);

The friend function is thus ::FS::eek:perator<<(...).
#endif //CASE_H_
In my cpp file I include this function -
#include "Case.h"
using namespace FS;

std::eek:stream& operator<<(std::eek:stream& outStream, const Case& case1)

And here you define ::eek:perator<<(...). A totally unrelated
function. Either put this definition in namespace FS, or define
it:
std::eek:stream& FS::eek:perator<<( ... ) ...
 
J

James Kanze

On May 14, 1:37 pm, (e-mail address removed) wrote:
I tried -
std::eek:stream& FS::eek:perator<<(std::eek:stream& outStream, const Case&
case1)
{
return outStream << case1.name.toStdString() << " v" <<
case1.version;
}
but this gave me -
Case.cpp:49: error: ¿std::eek:stream& FS::eek:perator<<(std::eek:stream&, const
FS::Case&)¿ should have been declared inside ¿FS¿

A frequent problem. The friend declaration declared a function
::FS::eek:perator<<, but it declared it in the class scope, so it
is only visible in the class (or through ADL). The rules for
defining a function using the namespace qualifiers, as you do
here, require a visible declaration of the function.
Then I tried this -
#include "Case.h"
using namespace FS;

namespace FS
{
std::eek:stream& FS::eek:perator<<(std::eek:stream& outStream, const Case&
case1fg

You don't need the FS;;, here. You are in namespace FS, just as
you were in the class definition. (As far as I can tell, it's
not even legal. The standard seems to clearly say that if the
declarator-id is qualified, then it must refer to a previously
declared member of the namespace or class.)
{
return outStream << case1.name.toStdString() << " v" <<
case1.version;
}
}
and now it works. Not sure I fully understand though.

The various rules for name lookup are rather complicated, and
not particularly intuitive. In this case, the definition also
declares the function in FS (provided you drop the FS:: in it).

In general, the simplest rule is to simply ensure that
everything in namespace NS is defined and declared in that
namespace, and to not worry about qualified names (except for
classes) in declarations and definitions.
 
J

James Kanze

Well, the C++ standard seems to say that explicit
qualification as done above is fine:
7.3.1.2/2:
Members of a named namespace can also be defined outside that
namespace by explicit qualification
(3.4.3.2) of the name being defined, provided that the entity being
defined was already declared in the namespace and the definition
appears after the point of declaration in a namespace that encloses
the declaration¿s namespace.

The problem is that there is no visible declaration of the
entity in question :):FS::eek:perator<<). The friend declaration
does NOT inject the name into the surrounding namespace.
Also, 7.3.1.2/3:
If a friend declaration in a nonlocal class first declares a class or
function83) the friend class or function is a member of the innermost
enclosing namespace
This means that operator<< belongs to namespace FS and it can be
defined outside namespace FS by explicitly qualifying the name, like
FS::eek:perator<<

The issue isn't simple, mainly (I think) for want of a good,
well established vocabulary to explain it. The friend
declaration declares a function in the immediately surrounding
namespace, but the declaration itself is in class scope, and is
treated exactly as if it were in class scope. In his code,
there is no declaration of the operator<< in scope when he tries
to define it, so his declaration is illegal.

The simplest solution, in his case, is simply to open the
namespace, and define the function (using an unqualified name)
there. More generally, he might want to provide a declaration
in namespace scope in the header, e.g.:

namespace FS {
class Case ;
std::eek:stream& operator<<( std::eek:stream&, Case const& ) ;

class Case
{
// As in his original code...
} ;
}

If he does this, then he should be able to define

std::eek:stream& FS::eek:perator<<(
std::eek:stream& dest,
Case const& object )
{
// ...
}

without any problems.

(Another alternative would be to define the operator<< inline in
the friend declaration. This avoids the problem entirely.)
Which compiler did you try on? g++ 3.4 seems to compile this code
correctly, and I could'nt try on latest version. Comeau online,
however gives error. I'm not sure why.

In pre-standard days, a friend declaration injected the declared
name into file scope (there were no namespaces then). This
caused some problem, I forget what. (I've had it explained to
me three or four times; I just keep forgetting.) And the needed
functionality was subsumed by ADL, at least in the cases deemed
necessary. So the committee dropped the injection. Many older
compilers, however, still implement it; I think you'll find that
g++ changed with 4.0 (or something like that). (Of the
compilers I have handy, g++ 4.3.3 rejects it, but g++ 3.4.0, Sun
CC 5.8, and the VC++ from Visual Studios 8 all accept it.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top