How to reuse a friend operator?

Discussion in 'C++' started by Piotr Wyderski, Jul 23, 2004.

  1. Hello,

    is it possible to reuse a friend operator which is defined
    inside a class? I'd like to obtain the following behaviour:

    class integer {

    [...]

    integer operator +(signed long int v) const {

    // It somehow does its job
    }

    friend inline integer operator +(signed long int lhs, const integer&
    rhs) {

    return rhs.operator +(lhs); // This is allowed because of
    commutativity
    }

    friend inline integer operator +(signed int lhs, const integer& rhs) {

    return operator +(static_cast<signed long int>(lhs),rhs); // Here's
    the problem
    }
    };


    integer i;
    i + 10; // the first operator is used
    10L + i; // the second operator is used
    10 + i; // the third is used and then control is passed to the second one


    The problem is that the compiler (G++ 3.4) doesn't see the operators
    declared as friends. It displays a message that no operator can be matched
    and shows the list of alternatives, but only local operators are listed (in
    this example there's only one, in the real project there are 6). When the
    friend operators are declared in a standard way, i.e. outside of the class,
    everything works OK. So the question is: how to qualify the "operator +()"
    in the third operator's body to use the second one? None of these
    ad hoc approaches works:

    "return ::eek:perator +("
    "return integer::eek:perator +("
    "return friend operator +("

    Best regards
    Piotr Wyderski
     
    Piotr Wyderski, Jul 23, 2004
    #1
    1. Advertising

  2. Piotr Wyderski

    Ali Cehreli Guest

    On Thu, 22 Jul 2004 17:22:12 -0700, Piotr Wyderski wrote:

    > Hello,
    >
    > is it possible to reuse a friend operator which is defined inside a
    > class? I'd like to obtain the following behaviour:
    >
    > class integer {
    >
    > [...]
    >
    > integer operator +(signed long int v) const {
    >
    > // It somehow does its job
    > }
    >
    > friend inline integer operator +(signed long int lhs, const
    > integer&
    > rhs) {
    >
    > return rhs.operator +(lhs); // This is allowed because of
    > commutativity
    > }
    >
    > friend inline integer operator +(signed int lhs, const integer&
    > rhs) {
    >
    > return operator +(static_cast<signed long int>(lhs),rhs); //
    > Here's
    > the problem
    > }
    > };
    >
    >
    > integer i;
    > i + 10; // the first operator is used 10L + i; // the second operator is
    > used 10 + i; // the third is used and then control is passed to the
    > second one


    If the problem that you are trying to solve is adding an integer to a
    class either on the left or on the right hand side, the following is
    the traditional way of doing that:

    #include <iostream>

    using namespace std;

    class Integer
    {
    long value_;

    friend ostream & operator<< (ostream &, Integer const &);

    public:

    Integer(long value = 0)
    :
    value_(value)
    {}

    Integer & operator+= (Integer const & rhs)
    {
    value_ += rhs.value_;
    return *this;
    }
    };

    Integer operator+ (Integer const & lhs, Integer const & rhs)
    {
    Integer sum = lhs;
    sum += rhs;
    return sum;
    }

    ostream & operator<< (ostream & os, Integer const & i)
    {
    return os << i.value_;
    }

    int main()
    {
    Integer i(42);
    cout << (i + 10) << '\n'
    << (10L + i) << '\n'
    << (10 + i) << '\n'
    << (i + Integer(10)) << '\n';
    }

    Ali
     
    Ali Cehreli, Jul 23, 2004
    #2
    1. Advertising

  3. Ali Cehreli wrote:

    > If the problem that you are trying to solve is adding an integer to a
    > class either on the left or on the right hand side, the following is
    > the traditional way of doing that:


    No, the problem is not the question how to add an integer to a class.
    This way is very inefficient in this particular case, because it requires
    a temporary object, which is quite big here. I have a very efficient
    implementation of such operators and I want to use it. The problem
    is that when the friend operators are defined inside the "integer" class,
    the compiler doesn't allow to use another friend operator to implement
    the current one. When they are defined outside the class, it works.
    My question is: why does the compiler behave like that and how to
    get rid of this problem without moving the operators outside?
    Nobody knows? Have I found a hole in C++?
     
    Piotr Wyderski, Jul 24, 2004
    #3
  4. Ali Cehreli wrote:

    Never mind, I have just found a solution, so EOT.
     
    Piotr Wyderski, Jul 24, 2004
    #4
  5. "Piotr Wyderski" <> wrote...
    >
    > Ali Cehreli wrote:
    >
    > > If the problem that you are trying to solve is adding an integer to a
    > > class either on the left or on the right hand side, the following is
    > > the traditional way of doing that:

    >
    > No, the problem is not the question how to add an integer to a class.
    > This way is very inefficient in this particular case, because it requires
    > a temporary object, which is quite big here. I have a very efficient
    > implementation of such operators and I want to use it. The problem
    > is that when the friend operators are defined inside the "integer" class,
    > the compiler doesn't allow to use another friend operator to implement
    > the current one. When they are defined outside the class, it works.
    > My question is: why does the compiler behave like that and how to
    > get rid of this problem without moving the operators outside?
    > Nobody knows? Have I found a hole in C++?


    No, you have more likely found a bug in your compiler. This should
    work, at least according to 3.4.1/9:
    ---------
    struct A {
    friend int operator +(int, A) { return 42; }
    operator double() const { return (2 + *this) / 10.; }
    // ^^^^^^^^^ operator+ is used
    operator float() const { return operator+(5,A()) / 5.f; }
    // ^^^^^^^^^^ should also be found
    // during name lookup.
    };

    #include <iostream>

    int main() {
    A a;
    double d = a;
    std::cout << d << std::endl; // should print 4.2
    }
    --------
    The code compiles fine with Comeau C++.

    Victor
     
    Victor Bazarov, Jul 24, 2004
    #5
  6. Piotr Wyderski wrote:
    >
    > Victor Bazarov wrote:
    >
    > > The code compiles fine with Comeau C++.

    >
    > But consider this one, which is a simplified version of my problem:
    >
    > -----------8<-------------
    >
    > #include <iostream>
    >
    > class X {
    >
    > public:
    >
    > int v;
    >
    > int operator +(const int R) {
    >
    > return 3;
    > }
    >
    > friend inline int operator +(const int L, const X& R) {
    >
    > std::cout << "OK int";
    > return L;
    > }
    >
    > friend inline int operator +(const char L, const X& R) {
    >
    > std::cout << "OK char";
    > return operator +(static_cast<int>(L),R); // (*)
    > }
    > };
    >
    > int main(int argc, char *argv[]) {
    >
    > X x;
    > 'a'+x;
    > return 0;
    > }
    >
    > -----------8<-------------
    >
    > Desired behaviour is that the program displays "OK charOK int"
    >
    > GPP 3.4 displays:
    >
    > test.cpp: In function `int operator+(char, const X&)':
    > test.cpp:24: error: no matching function for call to `X::eek:perator+(int,
    > const X&)'
    > test.cpp:10: note: candidates are: int X::eek:perator+(int)
    >
    > If I change the (*) line to
    >
    > return ::eek:perator +(static_cast<int>(L),R);
    >
    > then it works. VC7.1 in all cases reports an error:
    >
    > error C3767: '+' matching function is not accessible
    > could be the friend function at 'vcadv.cpp(19)' : '+' [may be found via
    > argument-dependent lookup]
    >
    > or the friend function at 'vcadv.cpp(25)' : '+' [may be found via
    > argument-dependent lookup]
    >
    > Comeau on-line tester says:
    >
    > error: the global scope has no "operator+"
    > return ::eek:perator +(static_cast<int>(L),R);
    > ^
    >
    > When I remove "::", it says:
    >
    > error: a nonstatic member reference must be relative to a
    > specific object
    > return operator +(static_cast<int>(L),R);
    >
    > So which behaviour is correct according to the C++ standard? :)
    >
    > Best regards
    > Piotr Wyderski


    Here is my take on it:

    The scope resolution operator :: in the line (*) is needed because
    without it a member function
    int operator +(const int R)
    is found, and the global scope is not considered. The reason why
    the class scope takes precedence over the global scope is that the
    friend function is defined inside the class (see 11.4/5).

    So, unless you remove the member operator+ or move the definition
    of the friend operators outside of the class definition, you have
    to use the operator ::.

    Now, it appears that Comeau cannot find a friend function in
    the global scope unless this function is also declared in the
    global scope, and I /think/ it is right (3.4.3/4). Gcc seems to
    let you get away without a global declaration.

    A few forward declarations just before the class defintion should
    help:
    class X;
    int operator +(char L, const X& R); //lose the superfluous "const"
    int operator +(int L, const X& R); //ditto

    (now it compiles with both Gcc and Comeau).

    Denis
     
    Denis Remezov, Jul 24, 2004
    #6
  7. Victor Bazarov wrote:

    > The code compiles fine with Comeau C++.


    But consider this one, which is a simplified version of my problem:


    -----------8<-------------

    #include <iostream>


    class X {

    public:

    int v;

    int operator +(const int R) {

    return 3;
    }

    friend inline int operator +(const int L, const X& R) {

    std::cout << "OK int";
    return L;
    }

    friend inline int operator +(const char L, const X& R) {

    std::cout << "OK char";
    return operator +(static_cast<int>(L),R); // (*)
    }
    };

    int main(int argc, char *argv[]) {

    X x;
    'a'+x;
    return 0;
    }


    -----------8<-------------

    Desired behaviour is that the program displays "OK charOK int"

    GPP 3.4 displays:

    test.cpp: In function `int operator+(char, const X&)':
    test.cpp:24: error: no matching function for call to `X::eek:perator+(int,
    const X&)'
    test.cpp:10: note: candidates are: int X::eek:perator+(int)

    If I change the (*) line to

    return ::eek:perator +(static_cast<int>(L),R);

    then it works. VC7.1 in all cases reports an error:

    error C3767: '+' matching function is not accessible
    could be the friend function at 'vcadv.cpp(19)' : '+' [may be found via
    argument-dependent lookup]

    or the friend function at 'vcadv.cpp(25)' : '+' [may be found via
    argument-dependent lookup]

    Comeau on-line tester says:

    error: the global scope has no "operator+"
    return ::eek:perator +(static_cast<int>(L),R);
    ^

    When I remove "::", it says:

    error: a nonstatic member reference must be relative to a
    specific object
    return operator +(static_cast<int>(L),R);

    So which behaviour is correct according to the C++ standard? :)

    Best regards
    Piotr Wyderski
     
    Piotr Wyderski, Jul 24, 2004
    #7
  8. "Piotr Wyderski" <> wrote...
    >
    > Victor Bazarov wrote:
    >
    > > The code compiles fine with Comeau C++.

    >
    > But consider this one, which is a simplified version of my problem:
    >
    >
    > -----------8<-------------
    >
    > #include <iostream>
    >
    >
    > class X {
    >
    > public:
    >
    > int v;
    >
    > int operator +(const int R) {
    >
    > return 3;
    > }
    >
    > friend inline int operator +(const int L, const X& R) {
    >
    > std::cout << "OK int";
    > return L;
    > }
    >
    > friend inline int operator +(const char L, const X& R) {
    >
    > std::cout << "OK char";
    > return operator +(static_cast<int>(L),R); // (*)
    > }
    > };
    >
    > int main(int argc, char *argv[]) {
    >
    > X x;
    > 'a'+x;
    > return 0;
    > }
    >
    >
    > -----------8<-------------
    >
    > Desired behaviour is that the program displays "OK charOK int"
    >
    > GPP 3.4 displays:
    >
    > test.cpp: In function `int operator+(char, const X&)':
    > test.cpp:24: error: no matching function for call to `X::eek:perator+(int,
    > const X&)'
    > test.cpp:10: note: candidates are: int X::eek:perator+(int)
    >
    > If I change the (*) line to
    >
    > return ::eek:perator +(static_cast<int>(L),R);
    >
    > then it works. VC7.1 in all cases reports an error:
    >
    > error C3767: '+' matching function is not accessible
    > could be the friend function at 'vcadv.cpp(19)' : '+' [may be found via
    > argument-dependent lookup]
    >
    > or the friend function at 'vcadv.cpp(25)' : '+' [may be found via
    > argument-dependent lookup]
    >
    > Comeau on-line tester says:
    >
    > error: the global scope has no "operator+"
    > return ::eek:perator +(static_cast<int>(L),R);
    > ^
    >
    > When I remove "::", it says:
    >
    > error: a nonstatic member reference must be relative to a
    > specific object
    > return operator +(static_cast<int>(L),R);
    >
    > So which behaviour is correct according to the C++ standard? :)


    Good question.

    Name lookup is a complicated issue to say the least.

    As I read it, the Standard, 3.4.3/2, the last part of it, says that
    if during an ordinary unqualified name lookup a _member_ is found,
    then the associated namespaces namespaces and classes are not considered.

    Since your class has a member operator+, the rest of operators+ are not
    found.

    So, it is standard behaviour, there are work-arounds, do you want me to
    help you with them or do you already know them?

    Victor
     
    Victor Bazarov, Jul 24, 2004
    #8
  9. Victor Bazarov wrote:

    > As I read it, the Standard, 3.4.3/2, the last part of it, says that
    > if during an ordinary unqualified name lookup a _member_ is found,
    > then the associated namespaces namespaces and classes are not considered.


    I'll look closer at that chapter.

    > So, it is standard behaviour, there are work-arounds, do you want me to
    > help you with them or do you already know them?


    Well, of course, please describe the workarounds you know, maybe
    something would be new to me. However, the question is not how to
    fix it, becaue the most standard-conforming workaround is to move
    friends outside the class. The question is how to do it using internal
    friends, because:

    a) the C++ standard allows internal friends;
    b) it seems to be impossible to call one of them from another.

    If the above statement is true, then there is an imperfection
    in the name lookup algorithm and it should be fixed.

    Best regards
    Piotr Wyderski
     
    Piotr Wyderski, Jul 24, 2004
    #9
  10. Denis Remezov wrote:

    > The scope resolution operator :: in the line (*) is needed because
    > without it a member function
    > int operator +(const int R)
    > is found, and the global scope is not considered.


    Right, but why does VC reject it?

    > So, unless you remove the member operator+ or move the definition
    > of the friend operators outside of the class definition, you have
    > to use the operator ::.


    If it were the standard solution, it would be perfectly OK. But
    the two mentioned compilers refuse to accept this syntax. And
    I don't know which one is right... :)

    > A few forward declarations just before the class defintion should help:


    Yes, but it is not better than moving the friend operators outside.
    So, is it possible to keep internal friend operators without adding
    anything outside the class (scope qualifiers inside of it are OK)?
    It's a matter of honour, heh, because we don't want the standard
    to be holey, do we? ;-)

    Best regards
    Piotr Wyderski
     
    Piotr Wyderski, Jul 24, 2004
    #10
  11. "Piotr Wyderski" <> wrote...
    >
    > Victor Bazarov wrote:
    >
    > > As I read it, the Standard, 3.4.3/2, the last part of it, says that
    > > if during an ordinary unqualified name lookup a _member_ is found,
    > > then the associated namespaces namespaces and classes are not

    considered.
    >
    > I'll look closer at that chapter.
    >
    > > So, it is standard behaviour, there are work-arounds, do you want me to
    > > help you with them or do you already know them?

    >
    > Well, of course, please describe the workarounds you know, maybe
    > something would be new to me. However, the question is not how to


    If it not, then I won't bother.

    > fix it, becaue the most standard-conforming workaround is to move
    > friends outside the class. The question is how to do it using internal
    > friends, because:
    >
    > a) the C++ standard allows internal friends;
    > b) it seems to be impossible to call one of them from another.


    That's not necessarily true. The thing is that the member operator+
    _interferes_ with name lookup of the friend.

    >
    > If the above statement is true, then there is an imperfection
    > in the name lookup algorithm and it should be fixed.


    Try describing the problem in comp.std.c++, and possibly suggest the
    fix after studying the current behaviour in more detail.

    Victor
     
    Victor Bazarov, Jul 24, 2004
    #11
    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. tshad
    Replies:
    5
    Views:
    562
    Steve C. Orr [MVP, MCSD]
    May 17, 2005
  2. Hylander

    To reuse or not to reuse....

    Hylander, Feb 26, 2004, in forum: Java
    Replies:
    0
    Views:
    442
    Hylander
    Feb 26, 2004
  3. code reuse and design reuse

    , Feb 7, 2006, in forum: C Programming
    Replies:
    16
    Views:
    1,066
    Malcolm
    Feb 12, 2006
  4. jacob navia

    To reuse or not to reuse

    jacob navia, Nov 5, 2006, in forum: C Programming
    Replies:
    19
    Views:
    570
    Dave Thompson
    Dec 18, 2006
  5. Peter
    Replies:
    2
    Views:
    312
    Öö Tiib
    Jun 6, 2013
Loading...

Share This Page