Trouble with overloading << operator via friend

Discussion in 'C++' started by magnus.moraberg@gmail.com, May 14, 2009.

  1. Guest

    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.
     
    , May 14, 2009
    #1
    1. Advertising

  2. Guest

    On May 14, 1:37 pm, wrote:
    > 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.


    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...
     
    , May 14, 2009
    #2
    1. Advertising

  3. Neelesh Guest

    On May 14, 4:50 pm, wrote:
    > On May 14, 1:37 pm, wrote:
    >
    >
    >
    >
    >
    > > 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?

    >


    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)
    }
     
    Neelesh, May 14, 2009
    #3
  4. Neelesh Guest

    On May 14, 10:40 pm, Neelesh <> wrote:
    > On May 14, 4:50 pm, wrote:
    >
    >
    >
    >
    >
    > > On May 14, 1:37 pm, wrote:

    >
    > > > 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?

    >
    > 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.


    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.
     
    Neelesh, May 14, 2009
    #4
  5. James Kanze Guest

    On May 14, 1:37 pm, wrote:
    > 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);


    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<<( ... ) ...

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, May 15, 2009
    #5
  6. James Kanze Guest

    On May 14, 1:50 pm, wrote:
    > On May 14, 1:37 pm, 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.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, May 15, 2009
    #6
  7. James Kanze Guest

    On May 14, 7:40 pm, Neelesh <> wrote:
    > On May 14, 4:50 pm, wrote:
    > > On May 14, 1:37 pm, 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¿


    > 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.)

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, May 15, 2009
    #7
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Robert Wierschke
    Replies:
    3
    Views:
    11,090
    Louise56
    Aug 3, 2004
  2. John Smith
    Replies:
    2
    Views:
    434
    Ivan Vecerina
    Oct 6, 2004
  3. Philip Pemberton
    Replies:
    5
    Views:
    571
    DaKoadMunky
    Oct 24, 2004
  4. Replies:
    4
    Views:
    545
    Greg Comeau
    Sep 30, 2006
  5. Peter
    Replies:
    2
    Views:
    286
    Öö Tiib
    Jun 6, 2013
Loading...

Share This Page