Topic: Builtin operator overload resolution


Author: "Johannes Schaub (litb)" <schaub-johannes@web.de>
Date: Tue, 27 Oct 2009 17:39:10 CST
Raw View
Johannes Schaub (litb) wrote:

> Richard Corden wrote:
>
>> Hi,
>>
>> Johannes Schaub (litb) wrote:
>>> Hello there,
>>>
>>> Richard Corden wrote:
>>
>> [...]
>>
>>>> If double can convert to int for "operator%", then the enumeration can
>>>> too.  By this logic, 99% of the references to "or enumeration type" in
>>>> clause 5 are unnecessary!  I just don't believe that is the case.
>>>>
>>> I've wondered about this too. If enumerations will be converted to
>>> integers anyway - why the mentions of "enumeration type"? Either this is
>>> a defect, ...
>>
>> Personally, I find it very unlikely we would find *that* part to be a
>> defect.  (I'm not sure at all about the rest of this discussion though,
>> and I'm getting less and less sure about that with each post!)
>>
>>> ... or operands are *really* *not* converted.
>>
>> The operands are definitely converted.
>>
>> There may be a way of interpreting this that does work.  Up to now, I
>> had been considering 5/3 as a kind of shortened overload resolution.
>> But another way of looking at 5/3, is that it describes the requirements
>> for built-in operators where overload resolution is involved.
>> Specifically that:
>>
>> * Operands to built-ins must still comply with the rules in clause 5
>> * When checking class type operands, user-defined conversions are
>> considered.
>>
>> And then you do full overload resolution.
>>
> Ah this would make sense, i think. So 5/3 acts as a viability criteria for
> all those candidates. Although i find 5/3 could be more explicit with
> this, and there could be a link from 13.3.2 back to 5/3. Thanks for the
> insight mate, this surely helped me figure out the stuffs.
>
Moreover in C++0x, explicit conversion functions were introduced:

struct A { explicit operator bool(); };
int main() { !A(); }

Overload resolution would add candidate functions, including "operator!
(bool)", but A could not be implicitly converted to its parameter, because
the conversion function is considered only for direct initialization.

The description in clause 5 says for operator!: The operand is contextually
converted to bool. This seems to subtly interact with overload resolution,
to make it work. I think this may be what 5/3 means with that overload
resolution shouldn't change the rules of builtin operators. That is, even
though overload resolution says that the conversion models copy
initialization, in fact the conversion models direct initialization for
operator!'s parameter in C++0x.

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: CornedBee <wasti.redl@gmx.net>
Date: Wed, 21 Oct 2009 11:57:31 CST
Raw View
On Oct 20, 8:26 pm, Richard Corden <richard.cor...@gmail.com> wrote:
>
> Ok, it wasn't a perfect example, consider the following:
>
>    enum E {
>      E0
>    };
>
>    int operator%(short, E);
>
>    void foo () {
>      int i = 3.14 % E0;
>    }
>
> Should be ambiguous if the builtins are included (but isn't with
> g++/comeau) - they both generate that we're not ignoring the void return
> type.  We can even go a step further and make the overloaded operator a
> really bad match:
>
>    struct A {
>      A (E);
>    };
>
>    int operator%(short, A);
>
>    void foo ()
>    {
>      int i = 3.14 % E0;
>    }
>
> Again, both g++ and comeau just warn about the initialization of 'i'.

I believe that both should be indeed an error, but at the moment I
don't have access to my Clang.

>
> Bringing this a bit further.  Lets say that g++ and comeau are wrong,
> and we should allow the float to integral conversion for the the builtin
> candidates.  One of the reasons we reach clause 13 is because the
> operator is overloaded.

No, the reason we reach it is that one of the operands is a user-
defined type.

>  But that operator doesn't have to be related to
> the operands that we're using, so consider the following:
>
>    struct A {
>    };
>
>    void f1 () {
>      3.14 % E0;           // compile error, % not overloaded
>    }

No, simply ambiguous. Overload resolution is performed, but there are
only built-in candidates.

>
>    A operator%(A, A);     // unrelated overloaded operator
>
>    void f2 () {
>      3.14 % E0;           // ok % is overloaded
>    }
>
> This would be very surprising behaviour indeed.

It would be. But the operator overloading rules don't care about the
existence of overloads, only about the types of the arguments. If any
argument has user-defined type, then we enter the overloading dance.
Otherwise we follow the built-in rules.

Sebastian


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Johannes Schaub (litb)" <schaub-johannes@web.de>
Date: Wed, 21 Oct 2009 20:26:48 CST
Raw View
CornedBee wrote:

> On Oct 20, 8:26 pm, Richard Corden <richard.cor...@gmail.com> wrote:
>>
>> Ok, it wasn't a perfect example, consider the following:
>>
>>    enum E {
>>      E0
>>    };
>>
>>    int operator%(short, E);
>>
>>    void foo () {
>>      int i = 3.14 % E0;
>>    }
>>
>> Should be ambiguous if the builtins are included (but isn't with
>> g++/comeau) - they both generate that we're not ignoring the void return
>> type. We can even go a step further and make the overloaded operator a
>> really bad match:
>>
>>    struct A {
>>      A (E);
>>    };
>>
>>    int operator%(short, A);
>>
>>    void foo ()
>>    {
>>      int i = 3.14 % E0;
>>    }
>>
>> Again, both g++ and comeau just warn about the initialization of 'i'.
>
> I believe that both should be indeed an error, but at the moment I
> don't have access to my Clang.
>
Agreed that the second is an error. But not the first: E -> E is an exact
match (and is an identity conversio sequence). While E -> any_integral_type
is always worse than an exact match. Thus our own operator% is used.

>>
>> Bringing this a bit further.  Lets say that g++ and comeau are wrong,
>> and we should allow the float to integral conversion for the the builtin
>> candidates.  One of the reasons we reach clause 13 is because the
>> operator is overloaded.
>
> No, the reason we reach it is that one of the operands is a user-
> defined type.
>
Agreed.

>>  But that operator doesn't have to be related to
>> the operands that we're using, so consider the following:
>>
>>    struct A {
>>    };
>>
>>    void f1 () {
>>      3.14 % E0;           // compile error, % not overloaded
>>    }
>
> No, simply ambiguous. Overload resolution is performed, but there are
> only built-in candidates.
Agreed.

>
>>
>>    A operator%(A, A);     // unrelated overloaded operator
>>
>>    void f2 () {
>>      3.14 % E0;           // ok % is overloaded
>>    }
>>
>> This would be very surprising behaviour indeed.
>
> It would be. But the operator overloading rules don't care about the
> existence of overloads, only about the types of the arguments. If any
> argument has user-defined type, then we enter the overloading dance.
> Otherwise we follow the built-in rules.
>
Agreed.

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Richard Corden <richard.corden@gmail.com>
Date: Wed, 21 Oct 2009 20:26:48 CST
Raw View
CornedBee wrote:
> On Oct 20, 8:26 pm, Richard Corden <richard.cor...@gmail.com> wrote:
>> Ok, it wasn't a perfect example, consider the following:
>
>>    A operator%(A, A);     // unrelated overloaded operator
>>
>>    void f2 () {
>>      3.14 % E0;           // ok % is overloaded
>>    }
>>
>> This would be very surprising behaviour indeed.
>
> It would be. But the operator overloading rules don't care about the
> existence of overloads, only about the types of the arguments. If any
> argument has user-defined type, then we enter the overloading dance.
> Otherwise we follow the built-in rules.


I think in reality there is a mixture of the built-in rules and the
"overloading dance" together.

13.3.1.2 covers operands that have class or enumeration type, so we
know for sure that an enumeration operand will go down the overload
resolution road.  However, in clause 5 nearly every paragraph has:
"arithmetic or enumeration type", or "integral or enumeration type".

If double can convert to int for "operator%", then the enumeration can
too.  By this logic, 99% of the references to "or enumeration type" in
clause 5 are unnecessary!  I just don't believe that is the case.




I ran the following example with 3 different compilers:

 enum E { E0 };

 class A {
 public:
   A (E);
 };

 void operator%(short, A);

 int main ()
 {
   int i = 3.14 % E0;
 }


g++:
 error: void value not ignored as it ought to be

edg(comeau):
 error: a value of type "void" cannot be used to initialize
         an entity of type "int"

vc(2003):
 error C2296: '%' : illegal, left operand has type 'double'


This is consistent with g++ and edg doing the following:

1) Reject the builtin operator due to double operand
2) Perform overload resolution on remaining overloads

And vc doing the following:

1) Perform overload resolution on all overloads
2) Reject the builtin operator due to double operand


With the first approach, edg and g++ consider more code as being legal
than vc does.

What's clear, is that all compilers rejecting the double operand to
the built-in operator, the only question appears to be when they do
so.


Regards,

Richard


--
Richard Corden

[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Richard Corden <richard.corden@gmail.com>
Date: Fri, 23 Oct 2009 10:33:27 CST
Raw View
Hi,

Johannes Schaub (litb) wrote:
> CornedBee wrote:
>
>> On Oct 20, 8:26 pm, Richard Corden <richard.cor...@gmail.com> wrote:
>>> Ok, it wasn't a perfect example, consider the following:
>>>
>>>    enum E {
>>>      E0
>>>    };
>>>
>>>    int operator%(short, E);
>>>
>>>    void foo () {
>>>      int i = 3.14 % E0;
>>>    }
>>>

[...]

>> I believe that both should be indeed an error, but at the moment I
>> don't have access to my Clang.
>>
> Agreed that the second is an error. But not the first: E -> E is an exact
> match (and is an identity conversio sequence). While E -> any_integral_type
> is always worse than an exact match. Thus our own operator% is used.

Your right, this example would compile anyway!  I was treating the
conversion from E0 to int as an identity conversion.

[...]

>>>  But that operator doesn't have to be related to
>>> the operands that we're using, so consider the following:
>>>
>>>    struct A {
>>>    };
>>>
>>>    void f1 () {
>>>      3.14 % E0;           // compile error, % not overloaded
>>>    }
>> No, simply ambiguous. Overload resolution is performed, but there are
>> only built-in candidates.
> Agreed.

As I said in my reply to CornedBee:  If, as is suggested, the only rules
applying to class and enumeration types are the overload rules, then
every mention of "or enumeration type" in the rest of clause 5 is
redundant,  since as you point out here, the enum type would be promoted
to an integral type.

The implementations I've checked so far all appear to reject the
built-in operator.  They do, however, vary on *when* they do the
rejection.  (g++/edg before overload resolution, vc after).

IMHO, we are left with the following choices:

(a) the current standard has lots of redundant text in it and 3 of the
     more popular compiler front ends are wrong, or


(b) it is too much of a simplification to say that overloading is the
     only way that the appropriate function is selected and the built-in
     rules *do* apply to the operand types before they are converted.



Cheers,

Richard


--
Richard Corden

[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Johannes Schaub (litb)" <schaub-johannes@web.de>
Date: Fri, 23 Oct 2009 10:33:39 CST
Raw View
Hello there,

Richard Corden wrote:

> CornedBee wrote:
>> On Oct 20, 8:26 pm, Richard Corden <richard.cor...@gmail.com> wrote:
>>> Ok, it wasn't a perfect example, consider the following:
>>
>>>    A operator%(A, A);     // unrelated overloaded operator
>>>
>>>    void f2 () {
>>>      3.14 % E0;           // ok % is overloaded
>>>    }
>>>
>>> This would be very surprising behaviour indeed.
>>
>> It would be. But the operator overloading rules don't care about the
>> existence of overloads, only about the types of the arguments. If any
>> argument has user-defined type, then we enter the overloading dance.
>> Otherwise we follow the built-in rules.
>
>
> I think in reality there is a mixture of the built-in rules and the
> "overloading dance" together.
>
> 13.3.1.2 covers operands that have class or enumeration type, so we
> know for sure that an enumeration operand will go down the overload
> resolution road.  However, in clause 5 nearly every paragraph has:
> "arithmetic or enumeration type", or "integral or enumeration type".
>
> If double can convert to int for "operator%", then the enumeration can
> too.  By this logic, 99% of the references to "or enumeration type" in
> clause 5 are unnecessary!  I just don't believe that is the case.
>
I've wondered about this too. If enumerations will be converted to integers
anyway - why the mentions of "enumeration type"? Either this is a
defect, or
operands are *really* *not* converted. But even if that's the case,
there is
no reason why user defined conversions are done, and standard conversions
not. 13.3.1.2 does not seem to say that, and clause 5 doesn't seem to say
that either.

If anything in 13.3.1.2 or clause 5 is supposed to say that, here we indeed
have a defect.

>
>
>
> I ran the following example with 3 different compilers:
>
>  enum E { E0 };
>
>  class A {
>  public:
>    A (E);
>  };
>
>  void operator%(short, A);
>
>  int main ()
>  {
>    int i = 3.14 % E0;
>  }
>
>
> g++:
>  error: void value not ignored as it ought to be
>
> edg(comeau):
>  error: a value of type "void" cannot be used to initialize
>          an entity of type "int"
>
> vc(2003):
>  error C2296: '%' : illegal, left operand has type 'double'
>
>
> This is consistent with g++ and edg doing the following:
>
> 1) Reject the builtin operator due to double operand
> 2) Perform overload resolution on remaining overloads
>
> And vc doing the following:
>
> 1) Perform overload resolution on all overloads
> 2) Reject the builtin operator due to double operand
>
>
> With the first approach, edg and g++ consider more code as being legal
> than vc does.
>
> What's clear, is that all compilers rejecting the double operand to
> the built-in operator, the only question appears to be when they do
> so.
>
In the Standard there is no way this can be inferred, from what i can see.
Two options seem to be possible:

- The builtin operators are not candidates: This is not the case, because
13.3.1.2 says they *are* candidates, because double and enum can be
converted to integer types fine.

- The builtin operator candidares are not viable: This doesn't seem to be
the case too according to 13.3.2.

If somehow rules of clause 5 interact with this, it got to be mentioned
here.

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Johannes Schaub (litb)" <schaub-johannes@web.de>
Date: Sat, 24 Oct 2009 16:00:39 CST
Raw View
Richard Corden wrote:

> For some reason my previous post have had all appropriate quoting
> removed.  I'm therefore resending it:
> [...]
> We can even go a step further and make the overloaded operator a
> really bad match:
>
>    struct A {
>      A (E);
>    };
>
>    int operator%(short, A);
>
>    void foo ()
>    {
>      int i = 3.14 % E0;
>    }
>
> Again, both g++ and comeau just warn about the initialization of 'i'.
>
>
In fact, there is another constraint in 13.3.1.2/3 that i overlooked.
Namely
for the non-member candidates, if no operand has class type, then for any
operand that has enumeration type, the corresponding parameter must have
that enum type or a reference to it. So in that example, the "operator%"
should not be a candidate.

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Richard Corden <richard.corden@gmail.com>
Date: Mon, 26 Oct 2009 00:19:05 CST
Raw View
Hi,

Johannes Schaub (litb) wrote:
> Hello there,
>
> Richard Corden wrote:

[...]

>> If double can convert to int for "operator%", then the enumeration can
>> too.  By this logic, 99% of the references to "or enumeration type" in
>> clause 5 are unnecessary!  I just don't believe that is the case.
>>
> I've wondered about this too. If enumerations will be converted to integers
> anyway - why the mentions of "enumeration type"? Either this is a
> defect, ...

Personally, I find it very unlikely we would find *that* part to be a
defect.  (I'm not sure at all about the rest of this discussion though,
and I'm getting less and less sure about that with each post!)

> ... or operands are *really* *not* converted.

The operands are definitely converted.

There may be a way of interpreting this that does work.  Up to now, I
had been considering 5/3 as a kind of shortened overload resolution.
But another way of looking at 5/3, is that it describes the requirements
for built-in operators where overload resolution is involved.
Specifically that:

* Operands to built-ins must still comply with the rules in clause 5
* When checking class type operands, user-defined conversions are
considered.

And then you do full overload resolution.


> But even if that's the case,
> there is
> no reason why user defined conversions are done, and standard conversions
> not. 13.3.1.2 does not seem to say that, and clause 5 doesn't seem to say
> that either.

It does make sense as the technique to use when checking class type
operands against the rules in clause 5.




Cheers,

Richard



--
Richard Corden

[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Johannes Schaub (litb)" <schaub-johannes@web.de>
Date: Mon, 26 Oct 2009 13:13:08 CST
Raw View
Johannes Schaub (litb) wrote:

> Richard Corden wrote:
>
>>
>> Hi,
>>
>> Johannes Schaub (litb) wrote:
>>
>>> Hello,
>>>
>>> Richard Corden wrote:
>>>
>>>> Johannes Schaub (litb) wrote:
>>>>
>>>
>> [...]
>>
>> The operands to '%' shall be "integral or enumeration type" and so this
>>>> set of builtin operators are not added to the overload set, and this
>>>> explains why your user defined candidate is the one chosen.
>>>>
>>>> I thought that before considering the rules of section 5, operands are
>>> converted to the types of the built-in candidate chosen. There is this
>>> paragraph which sounds suspicious at 5/3:
>>>
>>> "Operator overloading shall not modify the rules for the built-in
>>> operators,
>>> that is, for operators applied to types for which they are defined by
>>> this Standard.
>>>
>>
>> This is basically what I was thinking of when I made my original point.
>> So the only question is: "Do we convert our types before we apply the
>> clause 5 rule?"
>>
> I wonder about another thing in this context. If the above *really* means
> that we cannot tweak the behavior of an operator for "types for which it
> is defined" then i wonder whether
>
> enum E {
>   E1 = 0
> };
> void operator+(E) { }
> int main() {
>   +E1;
> }
>
> is valid at all. In my interpretation, 5/3 forbids the user defined
> operator+ because the operands on which it is defined is according to
> 5.3.1/6:
>
> "The operand of the unary + operator shall have arithmetic, enumeration,
> or pointer type and the result is the value of the argument."
>
> More generally, address-of seems to be defined for any lvalue. 5/3 would
> make it invalid to provide an own operator& for ones own class-type then.
> So i don't think my interpretation is correct.
>

I was too fast saying this. The Standard contains normative wording that
explicitly allows defining operator=, + and & and comma for ones own class
and enumeration types in 13.5/6:

"It is not possible to change the precedence, grouping, or number of
operands of operators. The meaning of the operators =, (unary) &, and ,
(comma), predefined for each type, can be changed for specific class and
enumeration types by defining operator functions that implement these
operators."

This gives my first interpretation some sense again and shows that the above
operator+ for our enumeration is not valid, apparently, because it's not
explicitly allowed, and thus is disallowed by the more general rule in
clause 5. This would then also provide a reason for the seemingly redundant
requirements of ".. or enumeration type" in clause 5 even though enums are
converted to integers anyway: The reason is to forbid the user to define
unary+, binary+ etc for enumerations and thereby overwriting the predefined
meaning.


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Johannes Schaub (litb)" <schaub-johannes@web.de>
Date: Mon, 26 Oct 2009 18:15:37 CST
Raw View
Richard Corden wrote:

> Hi,
>
> Johannes Schaub (litb) wrote:
>> Hello there,
>>
>> Richard Corden wrote:
>
> [...]
>
>>> If double can convert to int for "operator%", then the enumeration can
>>> too.  By this logic, 99% of the references to "or enumeration type" in
>>> clause 5 are unnecessary!  I just don't believe that is the case.
>>>
>> I've wondered about this too. If enumerations will be converted to
>> integers anyway - why the mentions of "enumeration type"? Either this is
>> a defect, ...
>
> Personally, I find it very unlikely we would find *that* part to be a
> defect.  (I'm not sure at all about the rest of this discussion though,
> and I'm getting less and less sure about that with each post!)
>
>> ... or operands are *really* *not* converted.
>
> The operands are definitely converted.
>
> There may be a way of interpreting this that does work.  Up to now, I
> had been considering 5/3 as a kind of shortened overload resolution.
> But another way of looking at 5/3, is that it describes the requirements
> for built-in operators where overload resolution is involved.
> Specifically that:
>
> * Operands to built-ins must still comply with the rules in clause 5
> * When checking class type operands, user-defined conversions are
> considered.
>
> And then you do full overload resolution.
>
Ah this would make sense, i think. So 5/3 acts as a viability criteria for
all those candidates. Although i find 5/3 could be more explicit with this,
and there could be a link from 13.3.2 back to 5/3. Thanks for the insight
mate, this surely helped me figure out the stuffs.

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: CornedBee <wasti.redl@gmx.net>
Date: Wed, 14 Oct 2009 13:26:41 CST
Raw View
On Oct 12, 9:13 pm, "Johannes Schaub (litb)" <schaub-johan...@web.de>
wrote:
> Converting double to long double is not a promotion according to section 4.6
> in the Standard. Only converting float to double is a (floating-point)
> promotion. Moreover, if i do it like in the following, the call is ambiguous
> with all compilers
>
> void f(int, int);
> void f(long, int);
> // ...
> void f(long double, one_death_);
>
> int main() { f(3.14, one); }
>
> My understanding was that this scenario is equivalent to the expression
> "3.14 % one" using built-in overload candidates with respect to finding a
> winner.
>

It should be. I repeated the test adding every built-in candidate and
go the same result as you, i.e. the operator overload resolution
appears to behave differently from the function overload resolution
for GCC. Clang treats the two exactly the same (same code path, even)
and will report ambiguity for both operators and functions.
I see no reason in the standard to treat operator usage different from
function calls once the candidate sets have been determined.

Sebastian

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Richard Corden <richard.corden@gmail.com>
Date: Thu, 15 Oct 2009 22:15:58 CST
Raw View
Hi,

Johannes Schaub (litb) wrote:

[...]

> int main() { 3.14 % one; }
>
> I expected that the implementation adds the following candidates according
> to 13.3.1/2 (listing only int for the second parameter. of course in reality
> it would take other ones too - but int is the best match):
>
> %(int, int)
> %(long, int)
> %(unsigned int, int)
> %(unsigned long, int)

I believe this may where things are going wrong.  If the builtin
operators are to be used, then they must conform to the requirements for
the operands.  5.6/2 has:

   The operands of * and / shall have arithmetic or enumeration type; the
   operands of % shall have integral or enumeration type.

The operands to '%' shall be "integral or enumeration type" and so this
set of builtin operators are not added to the overload set, and this
explains why your user defined candidate is the one chosen.



Cheers,

Richard


--
Richard Corden

[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Nikolay Ivchenkov <tsoae@mail.ru>
Date: Fri, 16 Oct 2009 09:34:24 CST
Raw View
On 16 Oct, 08:15, Richard Corden <richard.cor...@gmail.com> wrote:
>
> The operands to '%' shall be "integral or enumeration type" and so this
> set of builtin operators are not added to the overload set, and this
> explains why your user defined candidate is the one chosen.

This is bad explanation. Subclause 13.3.1.2 describes how overload
resolution is used to select appropriate built-in operator or used-
defined operator function. In particular, built-in candidates are
defined as follows:

"For the operator ,, the unary operator &, or the operator ->, the
built-in candidates set is empty. For all other operators, the built-
in candidates include all of the candidate operator functions defined
in 13.6 that, compared to the given operator,
-- have the same operator name, and
-- accept the same number of operands, and
-- accept operand types to which the given operand or operands can be
converted according to 13.3.3.1, and
-- do not have the same parameter type list as any non-template non-
member candidate."

In the case illustrated above the candidate operator%(int, int) - see
13.6/17 - satisfies all these conditions:
1) it has the same operator name,
2) it accepts 2 operands,
3) according to 13.3.3.1 there are exist implicit conversion sequences
those convert double to int and one_ to int,
4) it doesn't have the same parameter type list as any non-template
non-member candidate.

And now we have to apply 13.3.1.2/6: "The set of candidate functions
for overload resolution is the union of the member candidates, the
nonmember candidates, and the built-in candidates."

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Johannes Schaub (litb)" <schaub-johannes@web.de>
Date: Fri, 16 Oct 2009 09:34:41 CST
Raw View
Hello,

Richard Corden wrote:

>
> Hi,
>
> Johannes Schaub (litb) wrote:
>
> [...]
>
>> int main() { 3.14 % one; }
>>
>> I expected that the implementation adds the following candidates
>> according to 13.3.1/2 (listing only int for the second parameter. of
>> course in reality it would take other ones too - but int is the best
>> match):
>>
>> %(int, int)
>> %(long, int)
>> %(unsigned int, int)
>> %(unsigned long, int)
>
> I believe this may where things are going wrong.  If the builtin
> operators are to be used, then they must conform to the requirements for
> the operands.  5.6/2 has:
>
>    The operands of * and / shall have arithmetic or enumeration type; the
>    operands of % shall have integral or enumeration type.
>
> The operands to '%' shall be "integral or enumeration type" and so this
> set of builtin operators are not added to the overload set, and this
> explains why your user defined candidate is the one chosen.
>
I thought that before considering the rules of section 5, operands are
converted to the types of the built-in candidate chosen. There is this
paragraph which sounds suspicious at 5/3:

"Operator overloading shall not modify the rules for the built-in operators,
that is, for operators applied to types for which they are defined by this
Standard. However, these built-in operators participate in overload
resolution, and as part of that process user-defined conversions will be
considered where necessary to convert the operands to types appropriate for
the built-in operator. If a built-in operator is selected, such conversions
will be applied to the operands before the operation is considered further
according to the rules in clause 5; see 13.3.1.2, 13.6."

So the double is *first* converted to some integral type (if we *would* find
a winner). That only user defined conversions is mentioned does not in my
opinion exclude standard conversions (overload resolution section for these
does not appear to make any such restriction, and includes any and all
implicit conversion sequences), and is only for purpose of explanations,
because the whole matter is covered in overload sections again anyway.

Moreover, the rules of 5 are not considered during overload resolution, i
think. Overload section says the above too, more generally:

"If a built-in candidate is selected by overload resolution, the operands
are converted to the types of the corresponding parameters of the selected
operation function. Then the operator is treated as the corresponding built-
in operator and interpreted according to clause 5."

In any case, 13.3.1.2/3 should mention that only user defined or identity
conversion sequences are considered by overload resolution, but it says for
the builtin operator candidates

"accept operand types to which the given operand or operands can be
converted according to 13.3.3.1"

So i remain confused :(

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Richard Corden <richard.corden@gmail.com>
Date: Fri, 16 Oct 2009 13:02:38 CST
Raw View
Hi,

Johannes Schaub (litb) wrote:

> Hello,
>
> Richard Corden wrote:
>
>> Johannes Schaub (litb) wrote:
>>
>
[...]

The operands to '%' shall be "integral or enumeration type" and so this
>> set of builtin operators are not added to the overload set, and this
>> explains why your user defined candidate is the one chosen.
>>
>> I thought that before considering the rules of section 5, operands are
> converted to the types of the built-in candidate chosen. There is this
> paragraph which sounds suspicious at 5/3:
>
> "Operator overloading shall not modify the rules for the built-in
> operators,
> that is, for operators applied to types for which they are defined by this
> Standard.
>

This is basically what I was thinking of when I made my original point.  So
the only question is: "Do we convert our types before we apply the clause 5
rule?"

However, these built-in operators participate in overload
> resolution, and as part of that process user-defined conversions will be
> considered where necessary to convert the operands to types appropriate for
> the built-in operator. If a built-in operator is selected, such conversions
> will be applied to the operands before the operation is considered further
> according to the rules in clause 5; see 13.3.1.2, 13.6."
>
> So the double is *first* converted to some integral type (if we *would*
> find
> a winner). That only user defined conversions is mentioned does not in my
> opinion exclude standard conversions (overload resolution section for these
> does not appear to make any such restriction, and includes any and all
> implicit conversion sequences), and is only for purpose of explanations,
> because the whole matter is covered in overload sections again anyway.
>

And I think you've answered this yourself.  I really cannot see why the
standard would have explicitly said "user-defined conversion" rather than
just "conversion" if that is what they intended, ie, they've explicitly
added those words here.

Also, consider the following, if we go with overload resolution having
different rules, then strictly the first line in main would compile:

enum E {
 E0 = 1
};

int main ()
{
 3.14 % E0;  // legal because of overload rules?
 3.14 % 1;   // illegal
}


Pretty strange.


Moreover, the rules of 5 are not considered during overload resolution, i
> think.
>

If I understand how the standard works for operator expressions, clause 5 is
where it all starts (and so 5/3 will be considered).  It's actually from the
first few paragraphs of 5 that overload resolution section is referred to.

Cheers,

Richard



--
Richard Corden

[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Johannes Schaub (litb)" <schaub-johannes@web.de>
Date: Fri, 16 Oct 2009 23:31:38 CST
Raw View
Richard Corden wrote:

>
> Hi,
>
> Johannes Schaub (litb) wrote:
>
>> Hello,
>>
>> Richard Corden wrote:
>>
>>> Johannes Schaub (litb) wrote:
>>>
>>
> [...]
>
> The operands to '%' shall be "integral or enumeration type" and so this
>>> set of builtin operators are not added to the overload set, and this
>>> explains why your user defined candidate is the one chosen.
>>>
>>> I thought that before considering the rules of section 5, operands are
>> converted to the types of the built-in candidate chosen. There is this
>> paragraph which sounds suspicious at 5/3:
>>
>> "Operator overloading shall not modify the rules for the built-in
>> operators,
>> that is, for operators applied to types for which they are defined by
>> this Standard.
>>
>
> This is basically what I was thinking of when I made my original point.
> So the only question is: "Do we convert our types before we apply the
> clause 5 rule?"
>
I wonder about another thing in this context. If the above *really* means
that we cannot tweak the behavior of an operator for "types for which it is
defined" then i wonder whether

enum E {
  E1 = 0
};
void operator+(E) { }
int main() {
  +E1;
}

is valid at all. In my interpretation, 5/3 forbids the user defined
operator+ because the operands on which it is defined is according to
5.3.1/6:

"The operand of the unary + operator shall have arithmetic, enumeration, or
pointer type and the result is the value of the argument."

More generally, address-of seems to be defined for any lvalue. 5/3 would
make it invalid to provide an own operator& for ones own class-type then. So
i don't think my interpretation is correct.

Another possibility is that by "rules" it just means the semantics by each
built-in operator itself. So that for example operator overloading must not
get rid of the sequence point and short-curcuit for operator&& if a built-in
operator was selected. (I think this may be an important requirement,
because possible conversions involved for selecting op&& will surely have to
be properly sequenced. So the impl cannot just do: "overload-resolution,
convert, call built-in op", but has to interleave them so that built-in
operator semantics are still preserved). This sounds plausible.


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Johannes Schaub (litb)" <schaub-johannes@web.de>
Date: Fri, 16 Oct 2009 23:31:18 CST
Raw View
Richard Corden wrote:

>
> Hi,
>
> Johannes Schaub (litb) wrote:
>
>> Hello,
>>
>> Richard Corden wrote:
>>
>>> Johannes Schaub (litb) wrote:
>>>
>>
> [...]
>
> The operands to '%' shall be "integral or enumeration type" and so this
>>> set of builtin operators are not added to the overload set, and this
>>> explains why your user defined candidate is the one chosen.
>>>
>>> I thought that before considering the rules of section 5, operands are
>> converted to the types of the built-in candidate chosen. There is this
>> paragraph which sounds suspicious at 5/3:
>>
>> "Operator overloading shall not modify the rules for the built-in
>> operators,
>> that is, for operators applied to types for which they are defined by
>> this Standard.
>>
>
> This is basically what I was thinking of when I made my original point.
> So the only question is: "Do we convert our types before we apply the
> clause 5 rule?"
>
Of course i'm equally lost in the Standards wording, but here is what i was
thinking about it: Operator overloading can't change the rules (=meaning)
for "10 % 2", because the %operator is defined for those operand types. To
be able to change the meaning of it, at least one user defined type has to
appear as operand. And to be able for that expression to resolve to a built-
in operator, a user defined conversion is needed for that operand at least.
This, i think, is the reason that only user defined conversions are
mentioned there. That in addition other conversions can happen is not
forbidden here.


> However, these built-in operators participate in overload
>> resolution, and as part of that process user-defined conversions will be
>> considered where necessary to convert the operands to types appropriate
>> for the built-in operator. If a built-in operator is selected, such
>> conversions will be applied to the operands before the operation is
>> considered further according to the rules in clause 5; see 13.3.1.2,
>> 13.6."
>>
>> So the double is *first* converted to some integral type (if we *would*
>> find
>> a winner). That only user defined conversions is mentioned does not in my
>> opinion exclude standard conversions (overload resolution section for
>> these does not appear to make any such restriction, and includes any and
>> all implicit conversion sequences), and is only for purpose of
>> explanations, because the whole matter is covered in overload sections
>> again anyway.
>>
>
> And I think you've answered this yourself.  I really cannot see why the
> standard would have explicitly said "user-defined conversion" rather than
> just "conversion" if that is what they intended, ie, they've explicitly
> added those words here.
>
> Also, consider the following, if we go with overload resolution having
> different rules, then strictly the first line in main would compile:
>
> enum E {
>  E0 = 1
> };
>
> int main ()
> {
>  3.14 % E0;  // legal because of overload rules?
>
No, it is ambiguous: The second parameter is not, because it has some type
that it promotes to (instead of converting). But the first can be converted
to any promoted integral type equally well (note that floating point types
are *not* included as candidates for operator%). Clang agrees with me on
this. Note that 13.6/2 even explains that in a note:

"[Note: in all cases where a promoted integral type or promoted arithmetic
type is required, an operand of enumeration type will be acceptable by way
of the integral promotions. ]"


>  3.14 % 1;   // illegal
> }
>

This is plain illegal because overloading doesn't kick in because no operand
has class or enumeration type. Se we have no chance to do any conversion
before rules of 5 kick in. Clang also agrees.


>
> Pretty strange.
>
I agree :)

>
> Moreover, the rules of 5 are not considered during overload resolution, i
>> think.
>>
>
> If I understand how the standard works for operator expressions, clause 5
> is
> where it all starts (and so 5/3 will be considered).  It's actually from
> the first few paragraphs of 5 that overload resolution section is referred
> to.
>
I think it's like a big web of interlinked sections. We cannot figure out
where it all "starts", we can merely say what depends on what else. :)



--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: CornedBee <wasti.redl@gmx.net>
Date: Sun, 18 Oct 2009 08:51:28 CST
Raw View
It all starts with clause 5, Expressions. 5p1 and 5p2 are a note, so
the first real information is in 5p3, although the note in 5p2 goes a
long way to explain the intent:

5p2 (non-normative):
"Uses of overloaded operators are transformed into function calls as
described in 13.5. Overloaded operators obey the rules for syntax
specified in clause 5, but the requirements of operand type, lvalue,
and evaluation order are replaced by the rules for function call."

5p3 (normative):
"Clause 5 defines the effects of operators when applied to types for
which they have not been overloaded."

If the operator is overloaded, clause 5 takes a step back, until
overload resolution has decided on a specific version. Then, *if* a
built-in candidate is chosen, clause 5 takes charge again:
"However, these built-in operators participate in overload resolution,
and as part of that process user-defined conversions will be
considered where necessary to convert the operands to types
appropriate for the built-in operator. If a built-in operator is
selected, such con-
versions will be applied to the operands before the operation is
considered further according to the rules in
clause 5."

The last thing 5p3 says is that you can't define overloads that change
the meaning of operators for built-in types, i.e. it's illegal to
define "int operator %(int, int)".

So, forget clause 5 for the moment, and let's take a look at 13.5,
Overloaded Operators. It defines the syntax for operator functions,
but since % is a normal binary operator, there's no more to be said,
really. It references 13.3.1.2, though.

13.3.1.2p1 starts out by saying essentially the same thing as 5p3:
"If no operand of an operator in an expression has a type that is a
class or an enumeration, the operator is assumed to be a built-in
operator and interpreted according to clause 5."

But in the OP's example, this is not the case, since the second
operand most definitely has class type. So the rest of the clause
takes effect, and we collect an overload set. The set of member
candidates is empty, since T1 (the type of the left argument) is not a
class type. An unqualified lookup of operator% yields the non-member
candidate set, [operator%(long double, one_death_)]. Finally, the
built-in candidate set is constructed using the rules of 13.6, and
specifically 13.6p17, which asks for all functions operator%(L, R),
where L and R are all promoted integral types. Assuming the compiler
doesn't implement the long long extension or equivalent, this yields
16 overloads in the cross product [int, unsigned int, long, unsigned
long]^2. (With long long, you have 36 overloads.)

This yields a merged candidate list of 17 operators and the argument
list (rval double, lval one_).

Next, we filter by viability, according to 13.3.2. All operator
functions agree on the number of arguments. There exist valid implicit
conversion sequences to every single candidate, making all 17
candidates viable.

Now we select the best overload, according to 13.3.3. I list the
conversion sequences by argument, because I'm way too lazy to list 17
full sequences which are mostly duplicated. It's also more
understandable this way.
The second argument is an lvalue of type one_, and the possible
parameter types are [one_death_, int, uint, long, ulong]. A user-
defined conversion sequence (identity, user, identity) leads to
one_death_, the same to int. The other three are reached by a udcs
(identity, user, integral conversion).
It is clear that every candidate that has an integral type other than
int in the second parameter is worse than the other candidates.
They're gone. This leaves us with 5 candidates: (long double,
one_death_), (int, int), (uint, int), (long, int), (ulong, int). These
are all equivalent in the second parameter. Let's compare them in the
first.
The first argument is an rvalue of type double, and the possible
parameter types are [long double, int, uint, long, ulong]. This means
a floating point conversion for the long double, and a floating-
>integral conversion for everything else. These conversion all have
Conversion rank (see 13.3.3.1.1) and are thus all equally well suited.
The overload resolution is ambiguous between these candidates, the
program is illegal.
In other words, Clang is right, everyone else is wrong.

Sebastian

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Richard Corden <richard.corden@gmail.com>
Date: Mon, 19 Oct 2009 13:09:15 CST
Raw View
Hi,

Johannes Schaub (litb) wrote:
Richard Corden wrote:

[...]

This is basically what I was thinking of when I made my original
point. So the only question is: "Do we convert our types before we
apply the
clause 5 rule?"

I wonder about another thing in this context. If the above *really* means
that we cannot tweak the behavior of an operator for "types for which it is
defined" then i wonder whether

enum E {
 E1 = 0
};
void operator+(E) { }
int main() {
 +E1;
}

is valid at all. In my interpretation, 5/3 forbids the user defined
operator+ because the operands on which it is defined is according to
5.3.1/6:

"The operand of the unary + operator shall have arithmetic, enumeration, or
pointer type and the result is the value of the argument."

More generally, address-of seems to be defined for any lvalue. 5/3 would
make it invalid to provide an own operator& for ones own class-type then. So
i don't think my interpretation is correct.

Another possibility is that by "rules" it just means the semantics by each
built-in operator itself.

This is how I would interpret that.  Your overloaded operator only
needs to obey the standard function call rules, not the builtin
operator rules.


Cheers,

Richard



--
Richard Corden

[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Richard Corden <richard.corden@gmail.com>
Date: Mon, 19 Oct 2009 13:10:28 CST
Raw View
Hi,

Johannes Schaub (litb) wrote:
Richard Corden wrote:

[...]

Also, consider the following, if we go with overload resolution having
different rules, then strictly the first line in main would compile:

enum E {
 E0 = 1
};

int main ()
{
 3.14 % E0;  // legal because of overload rules?

No, it is ambiguous: The second parameter is not, because it has some type
that it promotes to (instead of converting). But the first can be converted
to any promoted integral type equally well (note that floating point types
are *not* included as candidates for operator%). Clang agrees with me on
this. Note that 13.6/2 even explains that in a note:

Ok, it wasn't a perfect example, consider the following:

 enum E {
   E0
 };

 int operator%(short, E);

 void foo () {
   int i = 3.14 % E0;
 }

Should be ambiguous if the builtins are included (but isn't with
g++/comeau) - they both generate that we're not ignoring the void
return type.  We can even go a step further and make the overloaded
operator a really bad match:

 struct A {
   A (E);
 };

 int operator%(short, A);

 void foo ()
 {
   int i = 3.14 % E0;
 }

Again, both g++ and comeau just warn about the initialization of 'i'.


Bringing this a bit further.  Lets say that g++ and comeau are wrong,
and we should allow the float to integral conversion for the the
builtin candidates.  One of the reasons we reach clause 13 is because
the operator is overloaded.  But that operator doesn't have to be
related to the operands that we're using, so consider the following:

 struct A {
 };

 void f1 () {
   3.14 % E0;           // compile error, % not overloaded
 }

 A operator%(A, A);     // unrelated overloaded operator

 void f2 () {
   3.14 % E0;           // ok % is overloaded
 }

This would be very surprising behaviour indeed.

I no longer have any doubt that the rules in clause 5 for operands to
builtins still apply even in the presence of overloads.  The only
question is if the standard clearly states this.

BTW - I would be curious as to what CLang does for the above cases.

Cheers,

Richard


--
Richard Corden

[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Richard Corden <richard.corden@gmail.com>
Date: Tue, 20 Oct 2009 12:26:15 CST
Raw View
For some reason my previous post have had all appropriate quoting
removed.  I'm therefore resending my post:



Johannes Schaub (litb) wrote:

> I wonder about another thing in this context. If the above *really* means
> that we cannot tweak the behavior of an operator for "types for which it is
> defined" then i wonder whether
>
> enum E {
>   E1 = 0
> };
> void operator+(E) { }
> int main() {
>   +E1;
> }
>
> is valid at all. In my interpretation, 5/3 forbids the user defined
> operator+ because the operands on which it is defined is according to
> 5.3.1/6:
>
> "The operand of the unary + operator shall have arithmetic, enumeration, or
> pointer type and the result is the value of the argument."
>
> More generally, address-of seems to be defined for any lvalue. 5/3 would
> make it invalid to provide an own operator& for ones own class-type then. So
> i don't think my interpretation is correct.
>
> Another possibility is that by "rules" it just means the semantics by each
> built-in operator itself.


This is how I would interpret that.  Your overloaded operator only needs
to obey the standard function call rules, not the builtin operator rules.

Regards,

Richard




--
Richard Corden

[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Richard Corden <richard.corden@gmail.com>
Date: Tue, 20 Oct 2009 12:26:33 CST
Raw View
For some reason my previous post have had all appropriate quoting
removed.  I'm therefore resending it:


Johannes Schaub (litb) wrote:
> Richard Corden wrote:
>
>> Also, consider the following, if we go with overload resolution having
>> different rules, then strictly the first line in main would compile:
>>
>> enum E {
>>  E0 = 1
>> };
>>
>> int main ()
>> {
>>  3.14 % E0;  // legal because of overload rules?
>>
> No, it is ambiguous: The second parameter is not, because it has some type
> that it promotes to (instead of converting). But the first can be converted
> to any promoted integral type equally well (note that floating point types
> are *not* included as candidates for operator%). Clang agrees with me on
> this.


Ok, it wasn't a perfect example, consider the following:

   enum E {
     E0
   };

   int operator%(short, E);

   void foo () {
     int i = 3.14 % E0;
   }

Should be ambiguous if the builtins are included (but isn't with
g++/comeau) - they both generate that we're not ignoring the void return
type.  We can even go a step further and make the overloaded operator a
really bad match:

   struct A {
     A (E);
   };

   int operator%(short, A);

   void foo ()
   {
     int i = 3.14 % E0;
   }

Again, both g++ and comeau just warn about the initialization of 'i'.


Bringing this a bit further.  Lets say that g++ and comeau are wrong,
and we should allow the float to integral conversion for the the builtin
candidates.  One of the reasons we reach clause 13 is because the
operator is overloaded.  But that operator doesn't have to be related to
the operands that we're using, so consider the following:

   struct A {
   };

   void f1 () {
     3.14 % E0;           // compile error, % not overloaded
   }

   A operator%(A, A);     // unrelated overloaded operator

   void f2 () {
     3.14 % E0;           // ok % is overloaded
   }

This would be very surprising behaviour indeed.

I no longer have any doubt that the rules in clause 5 for operands to
builtins still apply even in the presence of overloads.  The only
question is if the standard clearly states this.

BTW - I would be curious as to what CLang does for the above cases.

Cheers,

Richard



--
Richard Corden

[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Johannes Schaub (litb)" <schaub-johannes@web.de>
Date: Sun, 11 Oct 2009 17:05:29 CST
Raw View
I've tested to see how operator% behaves when overloaded and called with a
class type argument with the following code


struct one_death_ { };
struct one_ {
   operator one_death_() { return one_death_(); }
   operator int() { return 1; }
} one;
long double operator%(long double a, one_death_) {
   return 0;
}

int main() { 3.14 % one; }

I expected that the implementation adds the following candidates according
to 13.3.1/2 (listing only int for the second parameter. of course in reality
it would take other ones too - but int is the best match):

%(int, int)
%(long, int)
%(unsigned int, int)
%(unsigned long, int)

Now adding the user defined candidate

%(long double, one_death_)

Using all those candidates, it seems to me that there is no best viable
function among them:

// same thing for all built-in candidates
%(int, int)
double -> int # conversion in standard conversion sequence
one_ -> int # user defined conversion sequence

%(long double, one_death_)
double -> long double # conversion in standard conversion sequence
one_ -> one_death_ # user defined conversion sequence

However, on the compilers i can try, comeau and GCC, both say that the
operator function wins against the built-in one. Is this a bug in these
compilers, or do i miss something?

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Francis Glassborow <francis.glassborow@btinternet.com>
Date: Mon, 12 Oct 2009 10:25:00 CST
Raw View
Johannes Schaub (litb) wrote:
> I've tested to see how operator% behaves when overloaded and called with a
> class type argument with the following code
>
>
> struct one_death_ { };
> struct one_ {
>    operator one_death_() { return one_death_(); }
>    operator int() { return 1; }
> } one;
> long double operator%(long double a, one_death_) {
>    return 0;
> }
>
> int main() { 3.14 % one; }
>
> I expected that the implementation adds the following candidates according
> to 13.3.1/2 (listing only int for the second parameter. of course in reality
> it would take other ones too - but int is the best match):
>
> %(int, int)
> %(long, int)
> %(unsigned int, int)
> %(unsigned long, int)
>
> Now adding the user defined candidate
>
> %(long double, one_death_)
>
> Using all those candidates, it seems to me that there is no best viable
> function among them:
>
> // same thing for all built-in candidates
> %(int, int)
> double -> int # conversion in standard conversion sequence
> one_ -> int # user defined conversion sequence
>
> %(long double, one_death_)
> double -> long double # conversion in standard conversion sequence
> one_ -> one_death_ # user defined conversion sequence
>
> However, on the compilers i can try, comeau and GCC, both say that the
> operator function wins against the built-in one. Is this a bug in these
> compilers, or do i miss something?
>

Promoting 3.14 from double to long double is preferred to converting
3.14 from double to int. I think you missed the difference between a
conversion and a promotion.

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Johannes Schaub (litb)" <schaub-johannes@web.de>
Date: Mon, 12 Oct 2009 13:13:45 CST
Raw View
Francis Glassborow wrote:

> Johannes Schaub (litb) wrote:
>> I've tested to see how operator% behaves when overloaded and called with
>> a class type argument with the following code
>>
>>
>> struct one_death_ { };
>> struct one_ {
>>    operator one_death_() { return one_death_(); }
>>    operator int() { return 1; }
>> } one;
>> long double operator%(long double a, one_death_) {
>>    return 0;
>> }
>>
>> int main() { 3.14 % one; }
>>
>> I expected that the implementation adds the following candidates
>> according to 13.3.1/2 (listing only int for the second parameter. of
>> course in reality it would take other ones too - but int is the best
>> match):
>>
>> %(int, int)
>> %(long, int)
>> %(unsigned int, int)
>> %(unsigned long, int)
>>
>> Now adding the user defined candidate
>>
>> %(long double, one_death_)
>>
>> Using all those candidates, it seems to me that there is no best viable
>> function among them:
>>
>> // same thing for all built-in candidates
>> %(int, int)
>> double -> int # conversion in standard conversion sequence
>> one_ -> int # user defined conversion sequence
>>
>> %(long double, one_death_)
>> double -> long double # conversion in standard conversion sequence
>> one_ -> one_death_ # user defined conversion sequence
>>
>> However, on the compilers i can try, comeau and GCC, both say that the
>> operator function wins against the built-in one. Is this a bug in these
>> compilers, or do i miss something?
>>
>
> Promoting 3.14 from double to long double is preferred to converting
> 3.14 from double to int. I think you missed the difference between a
> conversion and a promotion.
>
Converting double to long double is not a promotion according to section 4.6
in the Standard. Only converting float to double is a (floating-point)
promotion. Moreover, if i do it like in the following, the call is ambiguous
with all compilers

void f(int, int);
void f(long, int);
// ...
void f(long double, one_death_);

int main() { f(3.14, one); }

My understanding was that this scenario is equivalent to the expression
"3.14 % one" using built-in overload candidates with respect to finding a
winner.

Someone else reported same behavior on MSVC with regard to my original code.
I tested the code also on clang (knowing it only implements things
partially, though), and it behaves different to all other compilers, and
seems to agree with my expectations.

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: tohava <tohava@gmail.com>
Date: Mon, 12 Oct 2009 13:14:24 CST
Raw View
On Oct 12, 1:05 am, "Johannes Schaub (litb)" <schaub-johan...@web.de>
wrote:
>    operator one_death_() { return one_death_(); }
>    operator int() { return 1; }} one;

On Oct 12, 6:25 pm, Francis Glassborow
<francis.glassbo...@btinternet.com> wrote:
> Promoting 3.14 from double to long double is preferred to converting
> 3.14 from double to int. I think you missed the difference between a
> conversion and a promotion.

Just out of curiousity, if I remember correctly, these two operators
will always be considered conversions and not promotions. Has anyone
considered a way to mark such operators as promotions?


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]