operator+ overload resolution

Discussion in 'C++' started by Richard Hayden, Jul 9, 2004.

  1. Hi,

    I have the following code for example:

    /**********************************/

    #include <iostream>

    class C1 {
    public:
    int a;

    C1(int b) : a(b) { }

    int operator+(C1& o) {
    std::cout << "C1 Op+ CALLED";
    return a + o.a;
    }
    };

    class C2 : public C1 {
    public:
    C2(int b) : C1(b) { }

    operator int() const { return a; }

    int operator+(int o) {
    std::cout << "C2 Op+ CALLED";
    return o + a;
    }
    };

    int main(int argc, char** argv) {
    C2 o1(2);
    C2 o2(3);

    C2 o3 = o1 + o2;

    return 0;
    }

    /**********************************/

    Surprisingly this compiles (with g++) and outputs 'C2 Op+ CALLED'.
    However, surely this should not compile since the call to operator+ is
    surely ambiguous? int operator+(int o) called with an O2 requires one
    cast (C2 -> int, which is defined explicitly in the class) and so does
    int operator+(C1& o) (C2& -> C1&). What's going on here? Have I
    misunderstood function overload resolution? Is g++ implementing the
    standard incorrectly?

    Thanks,

    Richard Hayden.
     
    Richard Hayden, Jul 9, 2004
    #1
    1. Advertising

  2. Richard Hayden wrote in news:cckkeu$ccd$ in
    comp.lang.c++:

    > class C1 {


    > int operator+(C1& o) {


    > };
    >
    > class C2 : public C1 {


    > int operator+(int o) {


    > };
    >
    > int main(int argc, char** argv) {
    > C2 o1(2);
    > C2 o2(3);
    >
    > C2 o3 = o1 + o2;
    >
    > return 0;
    > }
    >
    > /**********************************/
    >
    > Surprisingly this compiles (with g++) and outputs 'C2 Op+ CALLED'.
    > However, surely this should not compile since the call to operator+ is
    > surely ambiguous? int operator+(int o) called with an O2 requires one
    > cast (C2 -> int, which is defined explicitly in the class) and so does
    > int operator+(C1& o) (C2& -> C1&). What's going on here? Have I
    > misunderstood function overload resolution? Is g++ implementing the
    > standard incorrectly?
    >


    Your analysis is missing a conversion from C2 to C1, you're analysing
    the right hand side only and ignoring the left hand side:

    operator +( C2, C2 ) is

    A) C2::eek:perator +( C2, C2 |-> int ) or

    B) C1::eek:perator +( C2 |-> C1, C2 |-> C1 )

    (A) requires a shorter conversion sequence in the first paramiter
    to operator + and is choosen.

    Rob.
    --
    http://www.victim-prime.dsl.pipex.com/
     
    Rob Williscroft, Jul 9, 2004
    #2
    1. Advertising

  3. Richard Hayden wrote:
    > ...
    > I have the following code for example:
    >
    > /**********************************/
    >
    > #include <iostream>
    >
    > class C1 {
    > public:
    > int a;
    >
    > C1(int b) : a(b) { }
    >
    > int operator+(C1& o) {
    > std::cout << "C1 Op+ CALLED";
    > return a + o.a;
    > }
    > };
    >
    > class C2 : public C1 {
    > public:
    > C2(int b) : C1(b) { }
    >
    > operator int() const { return a; }
    >
    > int operator+(int o) {
    > std::cout << "C2 Op+ CALLED";
    > return o + a;
    > }
    > };
    >
    > int main(int argc, char** argv) {
    > C2 o1(2);
    > C2 o2(3);
    >
    > C2 o3 = o1 + o2;
    >
    > return 0;
    > }
    >
    > /**********************************/
    >
    > Surprisingly this compiles (with g++) and outputs 'C2 Op+ CALLED'.
    > However, surely this should not compile since the call to operator+ is
    > surely ambiguous? int operator+(int o) called with an O2 requires one
    > cast (C2 -> int, which is defined explicitly in the class) and so does
    > int operator+(C1& o) (C2& -> C1&). What's going on here? Have I
    > misunderstood function overload resolution? Is g++ implementing the
    > standard incorrectly?
    > ...


    According to 13.3.1.2/3, the set of member candidates in this case is
    created by qualified lookup for 'C2::eek:perator+'. But this lookup will
    not find the inherited 'C1::eek:perator+' in 'C2' simply because it is
    hidden in 'C2'. I.e. 'C1::eek:perator+' is not even considered as a viable
    function. For this reason there's no ambiguity.

    The potential for ambiguity will appear if you add a 'using' declaration
    of 'C1::eek:perator+' to 'C2's definition

    using C1::eek:perator+;

    In this case 'C1::eek:perator+' will be found by name lookup and added to
    the set of viable functions. Calling this function will require two
    implicit conversions of 'reference binding' type (see 13.3.3.1.4): one
    for the implied object argument and another for the explicit argument of
    'C1::eek:perator+'. Both bind a 'C1&' to an object of 'C2' type. To me it
    looks like it should result in an ambiguity. Such bindings are given the
    'Conversion' rank, which means that 'C1::eek:perator+' requires worse
    conversion sequence for the implied object argument than 'C2::eek:perator+'
    (which requires no conversion). At the same time 'C2::eek:perator+'
    requires worse conversion sequence (user-defined) for the explicit
    argument than 'C1::eek:perator+'.

    However, Comeau accepts the modified code and chooses 'C1::eek:perator+'
    (!). Maybe I'm missing something...

    --
    Best regards,
    Andrey Tarasevich
     
    Andrey Tarasevich, Jul 9, 2004
    #3
  4. Rob Williscroft wrote in news:Xns952116F6E7409ukcoREMOVEfreenetrtw@
    130.133.1.4 in comp.lang.c++:

    > Your analysis is missing a conversion from C2 to C1, you're analysing
    > the right hand side only and ignoring the left hand side:
    >
    > operator +( C2, C2 ) is
    >
    > A) C2::eek:perator +( C2, C2 |-> int ) or
    >
    > B) C1::eek:perator +( C2 |-> C1, C2 |-> C1 )
    >
    > (A) requires a shorter conversion sequence in the first paramiter
    > to operator + and is choosen.
    >


    And I missed that C2::eek:perator + hiddes C1::eek:perator +,
    fortunatly Andrey Tarasevich didn't miss it.

    Rob.
    --
    http://www.victim-prime.dsl.pipex.com/
     
    Rob Williscroft, Jul 9, 2004
    #4
  5. Richard Hayden

    Mark Guest

    On Thu, 08 Jul 2004 18:39:42 -0700, Andrey Tarasevich
    <> wrote:

    [...]

    >
    >According to 13.3.1.2/3, the set of member candidates in this case is
    >created by qualified lookup for 'C2::eek:perator+'. But this lookup will
    >not find the inherited 'C1::eek:perator+' in 'C2' simply because it is
    >hidden in 'C2'. I.e. 'C1::eek:perator+' is not even considered as a viable
    >function. For this reason there's no ambiguity.
    >
    >The potential for ambiguity will appear if you add a 'using' declaration
    >of 'C1::eek:perator+' to 'C2's definition
    >
    > using C1::eek:perator+;
    >
    >In this case 'C1::eek:perator+' will be found by name lookup and added to
    >the set of viable functions. Calling this function will require two
    >implicit conversions of 'reference binding' type (see 13.3.3.1.4): one
    >for the implied object argument and another for the explicit argument of
    >'C1::eek:perator+'. Both bind a 'C1&' to an object of 'C2' type. To me it
    >looks like it should result in an ambiguity. Such bindings are given the
    >'Conversion' rank, which means that 'C1::eek:perator+' requires worse
    >conversion sequence for the implied object argument than 'C2::eek:perator+'
    >(which requires no conversion). At the same time 'C2::eek:perator+'
    >requires worse conversion sequence (user-defined) for the explicit
    >argument than 'C1::eek:perator+'.
    >

    My standard is not currently is not within arm's reach, nonetheless
    I'm not sure if I'm following the meaning behind 'worse conversion
    sequence' for the implied and the explict object.

    For C2 worse than C1 (the explicit argument), the fact that the OP
    only defined operator+ with an int parameter, required a casts of o2
    to an int to perform the addition. Yes/No? If yes, how's C2 worse
    than C1?

    For C1 worse than C2, perhaps I'm misunderstanding the meaning of
    'implied object argument'. Elaborate if you will.

    Thanks

    Mark
    --
    [ C++ FAQ: http://www.parashift.com/c -faq-lite/ ]
     
    Mark, Jul 9, 2004
    #5
  6. Mark wrote:
    >>
    >>According to 13.3.1.2/3, the set of member candidates in this case is
    >>created by qualified lookup for 'C2::eek:perator+'. But this lookup will
    >>not find the inherited 'C1::eek:perator+' in 'C2' simply because it is
    >>hidden in 'C2'. I.e. 'C1::eek:perator+' is not even considered as a viable
    >>function. For this reason there's no ambiguity.
    >>
    >>The potential for ambiguity will appear if you add a 'using' declaration
    >>of 'C1::eek:perator+' to 'C2's definition
    >>
    >> using C1::eek:perator+;
    >>
    >>In this case 'C1::eek:perator+' will be found by name lookup and added to
    >>the set of viable functions. Calling this function will require two
    >>implicit conversions of 'reference binding' type (see 13.3.3.1.4): one
    >>for the implied object argument and another for the explicit argument of
    >>'C1::eek:perator+'. Both bind a 'C1&' to an object of 'C2' type. To me it
    >>looks like it should result in an ambiguity. Such bindings are given the
    >>'Conversion' rank, which means that 'C1::eek:perator+' requires worse
    >>conversion sequence for the implied object argument than 'C2::eek:perator+'
    >>(which requires no conversion). At the same time 'C2::eek:perator+'
    >>requires worse conversion sequence (user-defined) for the explicit
    >>argument than 'C1::eek:perator+'.
    >>

    > My standard is not currently is not within arm's reach, nonetheless
    > I'm not sure if I'm following the meaning behind 'worse conversion
    > sequence' for the implied and the explict object.
    >
    > For C2 worse than C1 (the explicit argument), the fact that the OP
    > only defined operator+ with an int parameter, required a casts of o2
    > to an int to perform the addition. Yes/No? If yes, how's C2 worse
    > than C1?


    'C2::eek:perator+' is worse than 'C1::eek:perator+' because in case of
    'C2::eek:perator+' a conversion from explicit argument type 'C2' to
    explicit parameter type 'int' is required. This can only be done by a
    user-defined conversion in this case.

    In case of 'C1::eek:perator+' another conversion is required: from explicit
    argument type 'C2' to explicit parameter type 'C1&'. As far as I
    understand the standard, this conversion is treated as a standard one
    (even though it is not included in the list of "official" standard
    conversions). For this reason 'C1::eek:perator+' is "better" than
    'C2::eek:perator+' (standard conversions are always "better" than
    user-defined ones).

    > For C1 worse than C2, perhaps I'm misunderstanding the meaning of
    > 'implied object argument'. Elaborate if you will.


    Candidate member functions of class 'T' are considered to have an extra
    parameter referred to as 'implied object parameter'. This parameter has
    type 'T&' (or 'const/volatile T&' for const/volatile member functions).
    This parameter is bound to the object, for which the member function has
    been invoked. (That's what I referred to as 'implied object argument'.
    Not a good choice of words. The argument is not really 'implied'. I
    should've called it simply 'object argument'). Conversions that are
    required in order to bind this parameter are also taken into account by
    overload resolution. Once again, if I understood the standard correctly,
    in this case the implied object parameter for 'C1::eek:perator+' has type
    'C1&' and the object has type 'C2', i.e. a conversion from 'C2' to 'C1&'
    is required (ranked as 'Conversion'). The implied object parameter for
    'C2::eek:perator+' has type 'C2&' and and the object has type 'C2', i.e. no
    conversion is required (ranked as 'Exact match'). That's why
    'C2::eek:perator+' is "better" from this point of view ('Exact
    match'-ranked conversions are always "better" than 'Conversion'-ranked
    ones).

    It possible that the above explanation contains some mistakes caused by
    my misunderstanding of some portions of the standard. Comeau's behavior
    also suggests that. It would be great to get to the bottom of this issue.

    --
    Best regards,
    Andrey Tarasevich
     
    Andrey Tarasevich, Jul 9, 2004
    #6
  7. Andrey Tarasevich wrote in news: in
    comp.lang.c++:

    > According to 13.3.1.2/3, the set of member candidates in this case is
    > created by qualified lookup for 'C2::eek:perator+'. But this lookup will
    > not find the inherited 'C1::eek:perator+' in 'C2' simply because it is
    > hidden in 'C2'. I.e. 'C1::eek:perator+' is not even considered as a
    > viable function. For this reason there's no ambiguity.
    >
    > The potential for ambiguity will appear if you add a 'using'
    > declaration of 'C1::eek:perator+' to 'C2's definition
    >
    > using C1::eek:perator+;
    >
    > In this case 'C1::eek:perator+' will be found by name lookup and added to
    > the set of viable functions. Calling this function will require two
    > implicit conversions of 'reference binding' type (see 13.3.3.1.4): one
    > for the implied object argument and another for the explicit argument
    > of 'C1::eek:perator+'. Both bind a 'C1&' to an object of 'C2' type. To me
    > it looks like it should result in an ambiguity. Such bindings are
    > given the 'Conversion' rank, which means that 'C1::eek:perator+' requires
    > worse conversion sequence for the implied object argument than
    > 'C2::eek:perator+' (which requires no conversion). At the same time
    > 'C2::eek:perator+' requires worse conversion sequence (user-defined) for
    > the explicit argument than 'C1::eek:perator+'.
    >
    > However, Comeau accepts the modified code and chooses 'C1::eek:perator+'
    > (!). Maybe I'm missing something...
    >


    This is a good question, last night I just didn't know the answer, this
    morning, I thought "but isn't a (member) using declaration a bit like":

    class B
    {
    public:
    void f();
    };

    class D : B
    {
    public:
    // using B::f;
    void f () { B::f(); }
    }

    It took me about 1/2 an hour to find it in the standard:

    7.3.3/13: (Theusingdeclaration)

    For the purpose of overload resolution, the functions which are
    introduced by a using-declaration into a derived class will be treated
    as though they were members of the derived class. In particular, the
    implicit this parameter shall be treated as if it were a pointer to the
    derived class rather than to the base class. This has no effect on the
    type of the function, and in all other respects the function remains a
    member of the base class.

    So my "... a bit like" is correct (only) as far as overload
    resolution goes.

    Rob. -- http://www.victim-prime.dsl.pipex.com/
     
    Rob Williscroft, Jul 9, 2004
    #7
  8. Rob Williscroft wrote:
    > ...
    > 7.3.3/13: (Theusingdeclaration)
    >
    > For the purpose of overload resolution, the functions which are
    > introduced by a using-declaration into a derived class will be treated
    > as though they were members of the derived class. In particular, the
    > implicit this parameter shall be treated as if it were a pointer to the
    > derived class rather than to the base class. This has no effect on the
    > type of the function, and in all other respects the function remains a
    > member of the base class.
    > ...


    Yes, that explains the Comeau's behavior. The first sentence at least. I
    find everything after the first one a bit misleading since it talks
    about implict parameter as a _pointer_, while in overload resolution
    sections the standard defines the implied object parameter to be a
    _reference_.

    --
    Best regards,
    Andrey Tarasevich
     
    Andrey Tarasevich, Jul 9, 2004
    #8
    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. Eph0nk

    Error: Overload resolution failed

    Eph0nk, Oct 23, 2003, in forum: ASP .Net
    Replies:
    0
    Views:
    1,869
    Eph0nk
    Oct 23, 2003
  2. =?Utf-8?B?Q2FpbGlu?=
    Replies:
    0
    Views:
    1,609
    =?Utf-8?B?Q2FpbGlu?=
    Sep 9, 2005
  3. Wolfgang Jeltsch

    overload resolution

    Wolfgang Jeltsch, Aug 31, 2003, in forum: C++
    Replies:
    2
    Views:
    409
    Filipe Sousa
    Aug 31, 2003
  4. Piotre Ugrumov
    Replies:
    3
    Views:
    377
    Nick Hounsome
    Jan 25, 2004
  5. Ying-Chieh Liao

    function overload (not operator overload)

    Ying-Chieh Liao, Oct 11, 2004, in forum: Perl Misc
    Replies:
    3
    Views:
    259
    Sherm Pendley
    Oct 11, 2004
Loading...

Share This Page