Hello,

In the following code conversion from class A object to class B object
can be done in two ways: (1) by conversion function being member of
class A (line 6); (2) by conversion constructor being member of class B
(line 11). However conversion constructor is called. The sole way to
make A::operator B() to be called is removing ,,const'' qualifiers (at
lines 11 & 25). This causes that the only implicit conversion sequence
goes throughout conversion function (declared at line 6). Why? It seems
that according to 13.3 of ISO 14882 none of the sequences is better then
other. Thus ambiguity should occur causing error. Could somebody point
out what part of ISO 14882 causes that conversion constructor (and not
conv foo) should be chosen and why conv ctor is better then conv foo?

-- best regards

Cezary Noweta

======
#include <stdio.h>

struct B;

struct A {
operator const B() const; // Line 6
};

struct B {
B();
B(const A& a); // Line 11
const B& operator=(const B&) const;
};

A::operator const B() const
{
printf("conv foo called\n");
return(B());
}

B::B()
{
}

B::B(const A& a) // Line 25
{
printf("conv ctor called\n");
}

const B& B::operator=(const B&) const
{
return(this[0]);
}

const B foo(const A a)
{
return(a);
}

const B foo1(const A a)
{
return((const B)a);
}

const B foo2(const A a)
{
return(const B(a));
}

main()
{
const A a;
const B b;

b = foo(a);
b = foo1(a);
b = foo2(a);
}
======

Re: Compiler chooses conv ctor - why? by Alex

Alex
Sun Apr 13 06:07:41 CDT 2008

"Cezary H. Noweta" wrote;
> It seems that according to 13.3 of ISO 14882 none of the
> sequences is better then other. Thus ambiguity should occur
> causing error.

It seems like a compiler bug. I tried the folowing code and it
fails to compile with Comeau C++ as expected:

-------
struct B;

struct A {
operator B() const;
};

struct B {
B() {}
B(const A&) {}
};

A::operator B() const
{ return *((B*)0); }

void foo(B) {}

int main()
{
A a;
B b = a; // Bug! Should not compile.

foo(a); // OK. Fails to compile.

return 0;
}
-------

Alex



Re: Compiler chooses conv ctor - why? by David

David
Sun Apr 13 08:52:32 CDT 2008

>In the following code conversion from class A object to class B object
>can be done in two ways: (1) by conversion function being member of
>class A (line 6); (2) by conversion constructor being member of class B
>(line 11). However conversion constructor is called.

As Alex says, it would appear to be a compiler bug/quirk.

I suggest that you submit a bug report on it at the MS connect web
site and post a link back here to your report and we'll add a
verification vote to it.

Dave

Re: Compiler chooses conv ctor - why? by Cezary

Cezary
Mon Apr 14 02:26:24 CDT 2008

Alex Blekhman wrote:
> "Cezary H. Noweta" wrote;
>> It seems that according to 13.3 of ISO 14882 none of the
>> sequences is better then other. Thus ambiguity should occur
>> causing error.
>
> It seems like a compiler bug. I tried the folowing code and it
> fails to compile with Comeau C++ as expected:
>
> -------
> struct B;
>
> struct A {
> operator B() const;
> };
>
> struct B {
> B() {}
> B(const A&) {}
> };
>
> A::operator B() const
> { return *((B*)0); }
>
> void foo(B) {}
>
> int main()
> {
> A a;
> B b = a; // Bug! Should not compile.
>
> foo(a); // OK. Fails to compile.
>
> return 0;
> }
> -------

OK - thank you. I've tested above example with various compilers, which
produced several different effects:

MSC12.0, MSC14.0: #1: conversion constructor called; #2: error (like in
your comments)
Watcom 1.7.1: #1, #2: conversion function called
Intel 7.1, Gnu 3.4.4: #1, #2: conversion constructor called

Certainly, it seems to be a bug. Hopefully I'm not blind ;)

Removing of const qualifier from A::operator B() will drop away an
ambiguity, as according to 13.3.3.2[3], a qualification conversion of an
implicit object parameter (B& to const B&) makes the constructor worse
conversion sequence and the conversion function should be chosen. Is
this right? If so, then there is another (or the same) bug in MSC, which
chooses a conversion constructor in such case.

-- best regards

Cezary Noweta

Re: Compiler chooses conv ctor - why? by changliw

changliw
Mon Apr 14 03:27:42 CDT 2008

Hi Alex,
Could you please let me know why you think that this is a compiler bug? I
think that this is just a way of compiler implementing conversion. In VC++,
by default "B b=a" will call B's constructor "B(const A& a);" to perform
the conversion; you can use "explicit" keyword before "B(const A& a);" to
avoid such automatic constructor conversion, and then the conversion would
happen on "A::operator const B() const".

Hi Cezary,
Regarding your original question, why compiler choosing conv ctor, I think
that it is related to some implicit conversion rule for compiler. When
multiple conversion methods are detected, one must be chose as with the
first priority. Unfortunately the detail of this is undocumented. Based on
my test, I think that VC++ compiler by default uses conversion constructor
prior to operator conversion. If you comment the constructor "B(const A&
a);", you will find that all conversions use your operator conversion
"A::operator const B() const" instead.

We can find some clues from MSDB documents:
Drawbacks of Conversion Constructors
http://msdn2.microsoft.com/en-us/library/1zd41sht.aspx

As the article mentioned, if it is essential to retain full control, do not
declare any constructors that take a single argument; instead, define
"helper" functions to perform conversions. So I would recommend that you
refer to this article as the best practice when you program in VC++.

If you have any other questions or concerns, please feel free to let me
know.

Best regards,
Charles Wang
Microsoft Online Community Support
===========================================================
Delighting our customers is our #1 priority. We welcome your
comments and suggestions about how we can improve the
support we provide to you. Please feel free to let my manager
know what you think of the level of service provided. You can
send feedback directly to my manager at: msdnmg@microsoft.com.
===========================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#

notif
ications.

Note: The MSDN Managed Newsgroup support offering is for
non-urgent issues where an initial response from the community
or a Microsoft Support Engineer within 1 business day is acceptable.
Please note that each follow up response may take approximately
2 business days as the support professional working with you may
need further investigation to reach the most efficient resolution.
The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are

best
handled working with a dedicated Microsoft Support Engineer by
contacting Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
============================================================
This posting is provided "AS IS" with no warranties, and confers no

rights.
=========================================================



Re: Compiler chooses conv ctor - why? by Cezary

Cezary
Mon Apr 14 04:39:50 CDT 2008

Hello,

Charles Wang[MSFT] wrote:

> Could you please let me know why you think that this is a compiler
> bug? I think that this is just a way of compiler implementing
> conversion. In VC++, by default "B b=a" will call B's constructor
> "B(const A& a);" to perform the conversion; you can use "explicit"
> keyword before "B(const A& a);" to avoid such automatic constructor
> conversion, and then the conversion would happen on "A::operator
> const B() const".

Charles, thank you for the MSDN link. I've looked for an explanation of
such behaviour. Here it is. However, previously I've looked into ISO
14822 and have found no explanation. Those web pages have been finishing
my doubts.

I'm not very familiar with this ISO, as I've recently returned to C++
after a long period of time. However, I'll try to answer your question
and point out why this could be compiler bug. I'll refer to the original
ISO 14882 dated 2003-10-15, especially I'll not refer to the working
draft N2521 dated on February this year. Maybe something related
to this issue has changed after original ISO has been released, or maybe
I missed something. If somebody corrects me I'll be grateful.

1. By definition (8.5[12]) ,,B b=a;'' is copy-initialization (as
opposite to direct-initialization ,,B b(a);'' which drops away conv foos
and considers conv ctors only).

2. As class A and class B are not derived from each other, 8.5[14]
directs us to 13.3.1.4.

3. 13.3.1.4: candidate functions to invoke this task are conv ctor (
B::B(const A&); ), and conv foo ( A::operator B() const; ).

4. According to 13.3.3[1] we start at checking of parameter conversion
sequences, when selecting the best function. A parameter of conv ctor is
,,const A&'', and implicit object parameter of conv foo is ,,const A&''
too. Both of functions require the same qualification conversion, thus
both of them are indistinguishable from each other.

5. Next, according to the same 13.3.3[1], we land at the standard
conversion sequence of return types.

6. Both of foos return ,,B'', so no one is better then other, and
according to 13.3.3[2] the call (initialization) is ill-formed.

-- best regards

Cezary Noweta

Re: Compiler chooses conv ctor - why? by Alex

Alex
Mon Apr 14 05:54:34 CDT 2008

"Cezary H. Noweta" wrote:
> 4. According to 13.3.3[1] we start at checking of parameter
> conversion sequences, when selecting the best function. A
> parameter of conv ctor is ,,const A&'', and implicit object
> parameter of conv foo is ,,const A&'' too. Both of functions
> require the same qualification conversion, thus both of them are
> indistinguishable from each other.

Your analysis is correct. All I can add is that 13.3.1.4/2
reiterates what is said in 13.3.3/1:

<quote>
Note: this argument will be compared against the first parameter
of the constructors and against the implicit object parameter of
the conversion functions.
</quote>

Actually, it's a long-standing bug in VC++ that direct
initialization is not distinguished from copy initialization. The
following code demonstrates it:

struct Y {};

class X
{
public:
X() {}
X(Y) {}
private:
X(const X&);
};

int main()
{
Y y;
X x1 = y; // ill-formed, inaccessible cctor
X x2(y); // OK, direct initialization

return 0;
}

According to the standard `x1' instance cannot be constructed due
to inaccessible copy constructor (which may be eliminated during
construction of an object by the compiler, but the copy semantics
must be preserved anyway).

Alex



Re: Compiler chooses conv ctor - why? by Cezary

Cezary
Mon Apr 14 14:10:22 CDT 2008

Hello,

Alex Blekhman wrote:
> struct Y {};
>
> class X
> {
> public:
> X() {}
> X(Y) {}
> private:
> X(const X&);
> };
>
> int main()
> {
> Y y;
> X x1 = y; // ill-formed, inaccessible cctor
> X x2(y); // OK, direct initialization
>
> return 0;
> }

> According to the standard `x1' instance cannot be constructed due to
> inaccessible copy constructor (which may be eliminated during
> construction of an object by the compiler, but the copy semantics
> must be preserved anyway).

But this elimination is an element of the semantics (8.5[14]). Such
elimination involves an elimination of checking cctor access, doesn't it?

-- best regards

Cezary Noweta

Re: Compiler chooses conv ctor - why? by Victor

Victor
Mon Apr 14 14:25:45 CDT 2008

Cezary H. Noweta wrote:
> Hello,
>
> Alex Blekhman wrote:
>> struct Y {};
>>
>> class X
>> {
>> public:
>> X() {}
>> X(Y) {}
>> private:
>> X(const X&);
>> };
>>
>> int main()
>> {
>> Y y;
>> X x1 = y; // ill-formed, inaccessible cctor
>> X x2(y); // OK, direct initialization
>>
>> return 0;
>> }
>
>> According to the standard `x1' instance cannot be constructed due to
>> inaccessible copy constructor (which may be eliminated during
>> construction of an object by the compiler, but the copy semantics
>> must be preserved anyway).
>
> But this elimination is an element of the semantics (8.5[14]). Such
> elimination involves an elimination of checking cctor access, doesn't
> it?

Huh? Elimination of the copy is allowed, but not required. Even if it
is forgone (the compiler decides to optimize it away), the copy-ctor has
to be available _as_if_ it were used (12.8/15).

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask



Re: Compiler chooses conv ctor - why? by Cezary

Cezary
Mon Apr 14 21:09:55 CDT 2008

Hello,

Victor Bazarov wrote:
> Huh? Elimination of the copy is allowed, but not required. Even if
> it is forgone (the compiler decides to optimize it away), the
> copy-ctor has to be available _as_if_ it were used (12.8/15).

I know that checking of access or not would be stupid. It would involve
that the same program is ill-formed when not optimized and well-formed
when optimized. Assuming that access checking is an element of the copy
construction, and after 12.8[15] states that ,,an implementation is
allowed to omit the copy construction'' the situation has been not so
clear for me. Hopefully, newer working drafts have paragraph 12.8[16],
which unambiguously clears everything.

-- best regards

Cezary Noweta

Re: Compiler chooses conv ctor - why? by changliw

changliw
Tue Apr 15 08:30:48 CDT 2008

Hi Cezary,
Thank you for your explaining on this according to ISO standard. I think
that I understand your meaning. Your main concern should be that at least
the compiler should throw an warning to tell developers this potential
issue, right? Could you please give Microsoft feedback via
https://connect.microsoft.com/visualstudio so that our product team can
hear your voice and improve our product? You may also be able to get email
notifications when Microsoft product team responds you.

If you have any other questions or concerns, please feel free to let me
know. It is my pleasure to be of assistance.

Have a nice day!

Best regards,
Charles Wang
Microsoft Online Community Support
=========================================================
Delighting our customers is our #1 priority. We welcome your
comments and suggestions about how we can improve the
support we provide to you. Please feel free to let my manager
know what you think of the level of service provided. You can
send feedback directly to my manager at: msdnmg@microsoft.com.
=========================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
=========================================================



Re: Compiler chooses conv ctor - why? by Ben

Ben
Tue Apr 15 18:06:13 CDT 2008

Charles Wang[MSFT] wrote:
> Hi Cezary,
> Thank you for your explaining on this according to ISO standard. I
> think that I understand your meaning. Your main concern should be
> that at least the compiler should throw an warning to tell developers
> this potential issue, right? Could you please give Microsoft feedback

Really, the compiler should adopt the behavior specified by the standard and
provide a command-line parameter to allow programs relying on the old
behavior to compile. (like was done with forscope).

This "it's ok to depart from the standard as long as we document it" is why
programmers 'round the world are fed up with Microsoft. That happened with
IE DHTML support and look at the mess that made, now IE can't even follow
the standard if Microsoft wants it to.



Re: Compiler chooses conv ctor - why? by Cezary

Cezary
Wed Apr 16 04:49:23 CDT 2008

Hello,

Charles Wang[MSFT] wrote:
> Thank you for your explaining on this according to ISO standard. I think
> that I understand your meaning. Your main concern should be that at least
> the compiler should throw an warning to tell developers this potential
> issue, right? Could you please give Microsoft feedback via
> https://connect.microsoft.com/visualstudio so that our product team can
> hear your voice and improve our product? You may also be able to get email
> notifications when Microsoft product team responds you.

OK - but before, could somebody confirm that in the following code
(similar to the code provided by Alex but conv foo is without const
qualifier):

======
#include <stdio.h>

struct B;

struct A {
operator B(); /* implicit object parameter: A & */
};

struct B {
B() {}
B(const A&) { printf("conv ctor\n"); }
};

A::operator B()
{
printf("conv foo\n");
return (B());
}

void foo(B) {}

int main()
{
A a;
B b = a; /* 25 */

foo(a); /* 27 */

return 0;
}
======

conversion function should be chosen, as according to 13.3.3.2[3] of the
standard, it is better function due to qualification conversion
required by conversion constructor (non-const A& to const A&)?

-- best regards

Cezary Noweta

Re: Compiler chooses conv ctor - why? by Alex

Alex
Wed Apr 16 07:27:36 CDT 2008

"Cezary H. Noweta" wrote:
> OK - but before, could somebody confirm that in the following
> code (similar to the code provided by Alex but conv foo is
> without const qualifier):
>
> [...]
> struct A {
> operator B(); /* implicit object parameter: A & */

Actually, the implicit object parameter is of type: A*.

> conversion function should be chosen, as according to
> 13.3.3.2[3] of the standard, it is better function due to
> qualification conversion required by conversion constructor
> (non-const A& to const A&)?

Yes. You're correct.

Alex



Re: Compiler chooses conv ctor - why? by Cezary

Cezary
Wed Apr 16 08:09:06 CDT 2008

Hello,

Alex Blekhman wrote:
> "Cezary H. Noweta" wrote:
>> [...]

>> struct A {

>> operator B(); /* implicit object parameter: A & */

> Actually, the implicit object parameter is of type: A*.

I do not agree this time ;) 13.3.1[4] states that IOP is reference to cv
X, where cv is the qualification on the member function declaration.

Otherwise we would have two signatures: (A*) for conv foo, and (const
A&) for conv ctor. There is no implicit conv sequence for A=>A*, so conv
foo would be dropped at the beginning of the looking for the best
function process.

-- best regards

Cezary Noweta

Re: Compiler chooses conv ctor - why? by Alex

Alex
Wed Apr 16 08:42:35 CDT 2008

"Cezary H. Noweta" wrote:
> I do not agree this time ;) 13.3.1[4] states that IOP is
> reference to cv X, where cv is the qualification on the member
> function declaration.

Yes, you're right. I just used to call it hidden `this'. :)

Alex



Re: Compiler chooses conv ctor - why? by Victor

Victor
Wed Apr 16 09:13:14 CDT 2008

Alex Blekhman wrote:
> "Cezary H. Noweta" wrote:
>> I do not agree this time ;) 13.3.1[4] states that IOP is
>> reference to cv X, where cv is the qualification on the member
>> function declaration.
>
> Yes, you're right. I just used to call it hidden `this'. :)

You might want to start calling "hidden *this" :-)

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask