Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Is this valid -> part-2.

14 views
Skip to first unread message

Dhruv

unread,
Dec 18, 2003, 9:38:01 PM12/18/03
to
I'd like to know what the standard says about this, and in geneeral has
anyone experienced the need to use such a thing. The question is that can
I have 2 conversion operators defined as such:


template <typename T>
struct Type {
T data;
//ctors, etc...

operator T& () { return data; } ___[1]
operator T const& () { return data; } ___[2]
};


The main motivation for thinking about such a thing is that I want Type<>
to return a reference to data for non-const objects, and T const& for
const objects, but unfortunately, that's not how it works, but however,
when compilingwith g++3.2 it compiles fine, but gives warnings, wnad
that's what I'm worried about. generally speaking, the warnings suggest
that the correct conversion sequesce is called for non-const objects[1],
while [2] is called for const objects, which is quite natural, snce that
is the only one that can be called for non-const objects. However, what
I'm happy about is that g++ warns saying that for non-const objects,
sequence[1] is a better match than[2].

Here's the code:


#include <iostream>
#include <algorithm>

using namespace std;

//#define TREAT_ASSIGN_AS_INIT

//Use only for PODs.
template <typename T>
struct Same_Type {
T data;
bool initialized;
Same_Type () : initialized(false) { }
Same_Type (T const& _data) : data (_data), initialized(true) { }
#if defined TREAT_ASSIGN_AS_INIT
T& operator= (T const& _data) { initialized = true; data = _data; return data; }
#endif
operator T& () { assert (initialized);
return data; }

operator T const& () const { assert (initialized);
return data; }
};

#define CHECK(TYPE) Same_Type<TYPE>
//#define CHECK(TYPE) TYPE


struct verbose {
CHECK (int) value;
verbose () : value(0) { cout<<"verbose::verbose ()"<<endl; }
verbose (int _v) : value(_v) { cout<<"verbose::verbose ("<<_v<<')'<<endl; }
verbose (verbose const& _other)
{ cout<<"verbose::verbose (verbose const&)"<<value<<" = "<<_other.value<<endl;
value = _other.value; }
~verbose () { cout<<"verbose::~verbose () : "<<value<<endl; }
verbose& operator= (verbose const& _other)
{ cout<<"verbose::operator= () "<<value<<" = "<<_other.value<<endl;
value = _other.value; return *this; }
void swap (verbose& _other)
{ std::swap (value, _other.value);
cout<<"verbose::swap (verbose&) "<<_other.value<<" <-> "<<value<<endl; }

void call_mem_fun () { cout<<"verbose::call_mem_fun () : "<<value<<endl; }
};


verbose foo ()
{
verbose v (23);
return v;
}


int main ()
{
verbose v(45);
v = foo ();
}


Regards,
-Dhruv.


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Peter Koch Larsen

unread,
Dec 19, 2003, 9:15:21 AM12/19/03
to
"Dhruv" <dhru...@gmx.net> skrev i en meddelelse
news:pan.2003.12.18....@gmx.net...

> I'd like to know what the standard says about this, and in geneeral has
> anyone experienced the need to use such a thing. The question is that can
> I have 2 conversion operators defined as such:
>
>
> template <typename T>
> struct Type {
> T data;
> //ctors, etc...
>
> operator T& () { return data; } ___[1]
> operator T const& () { return data; } ___[2]
> };
>

[huge snip]

Hi Dhruv

I might be misunderstanding something, but what you want appears to me to
be:

operator T& () { return data; } ___[1]

operator T const& () const { return data; } ___[2]
*****

This way, you will get a const reference for const objects only.


Kind regards
Peter

Dhruv

unread,
Dec 20, 2003, 6:51:02 AM12/20/03
to
On Fri, 19 Dec 2003 09:15:21 -0500, Peter Koch Larsen wrote:

> "Dhruv" <dhru...@gmx.net> skrev i en meddelelse
> news:pan.2003.12.18....@gmx.net...
>> I'd like to know what the standard says about this, and in geneeral has
>> anyone experienced the need to use such a thing. The question is that can
>> I have 2 conversion operators defined as such:
>>
>>
>> template <typename T>
>> struct Type {
>> T data;
>> //ctors, etc...
>>
>> operator T& () { return data; } ___[1]
>> operator T const& () { return data; } ___[2]
>> };
>>
>
> [huge snip]
>
> Hi Dhruv
>
> I might be misunderstanding something, but what you want appears to me to
> be:
>
> operator T& () { return data; } ___[1]
> operator T const& () const { return data; } ___[2]
> *****
>
> This way, you will get a const reference for const objects only.

Yes, I *want* that to happen, but one of the side effects of that might be
that I get const reference for non-const objects as well? That's because
the compiler issues a waning saying that the [1] overload or whatever is a
better match than the [2] one, so I'm confused whether this is actually
legal code?

Regards,
-Dhruv.

Peter Koch Larsen

unread,
Dec 20, 2003, 12:38:12 PM12/20/03
to

"Dhruv" <dhru...@gmx.net> skrev i en meddelelse
news:pan.2003.12.20...@gmx.net...

This should work perfectly. Since overloads are not resolved based on the
returnvalue, the resolve of a operator T of a non-const Type< T > will
always resolve in the non-const version and vice versa. Try it out and post
again if You have problems.

Kind regards
Peter

John Potter

unread,
Dec 20, 2003, 2:59:05 PM12/20/03
to
On 20 Dec 2003 06:51:02 -0500, "Dhruv" <dhru...@gmx.net> wrote:

> On Fri, 19 Dec 2003 09:15:21 -0500, Peter Koch Larsen wrote:

> > "Dhruv" <dhru...@gmx.net> skrev i en meddelelse

> > news:pan.2003.12.18....@gmx.net...

> >> I'd like to know what the standard says about this, and in geneeral has
> >> anyone experienced the need to use such a thing. The question is that can
> >> I have 2 conversion operators defined as such:

> >> template <typename T>
> >> struct Type {
> >> T data;
> >> //ctors, etc...

> >> operator T& () { return data; } ___[1]
> >> operator T const& () { return data; } ___[2]
> >> };

> > I might be misunderstanding something, but what you want appears to me to
> > be:

> > operator T& () { return data; } ___[1]
> > operator T const& () const { return data; } ___[2]
> > *****

> > This way, you will get a const reference for const objects only.

> Yes, I *want* that to happen, but one of the side effects of that might be
> that I get const reference for non-const objects as well? That's because
> the compiler issues a waning saying that the [1] overload or whatever is a
> better match than the [2] one, so I'm confused whether this is actually
> legal code?

Note that you confused the issue by omitting the const at the top of
your original post and including it in the real code. Peter was
responding to your missing const not the warning.

If you check the warnings, you will see that they are simply telling you
that because the object is not const, the non-const version is picked
and then used to initialize the const reference in the operator<<. It
is the constness of the object not the desired reference that selects
the function.

John

Dhruv

unread,
Dec 20, 2003, 8:21:37 PM12/20/03
to
On Sat, 20 Dec 2003 12:38:12 -0500, Peter Koch Larsen wrote:

> This should work perfectly. Since overloads are not resolved based on the
> returnvalue, the resolve of a operator T of a non-const Type< T > will
> always resolve in the non-const version and vice versa. Try it out and post
> again if You have problems.


The thing is that it compiles fine, but it gives a warning. That's what's
bothering me. I get the feeling that I'm using some sort of behaviour that
might change when I least expect it to.

I need to get this confirmed:
Supppose I have 2 operator functions. one of them converts to T and the
other to const T, then, can they be overloaded?

Something like this:

operator T ();
operator T const ();

Is this legal overloading of operators? If so, how does the compiler know
which one to call when a cnversion is requested for seeing the value T.
ie, when the object is used as an r-value?

Regards,
-Dhruv.

Dhruv

unread,
Dec 20, 2003, 8:26:15 PM12/20/03
to

Oh! Thanks for pointing it out.

I meant the one that was in the code, with the const qualifier for the
function as well.

> If you check the warnings, you will see that they are simply telling you
> that because the object is not const, the non-const version is picked
> and then used to initialize the const reference in the operator<<. It
> is the constness of the object not the desired reference that selects
> the function.

That's what I'm worried about. Because operator T const& is a const member
function, it can be called on const As Well As non-const objects right? Or
have I succeeded in confusing myself? So, I wanted some sort of guarantee
that whenever I call operator conversion function on an object implicitly,
the correct ones should be chosen. ie. For non-const objects, operator T&,
and for const objects, operator T const&.

Is there some sort of rule that if you have 2 identical functions
overloaded by const qualification ONLY, then the const version will be
called ONLY for Const objects?

Regards,
-Dhruv.

John Potter

unread,
Dec 21, 2003, 6:43:05 AM12/21/03
to
On 20 Dec 2003 20:21:37 -0500, "Dhruv" <dhru...@gmx.net> wrote:

> operator T ();
> operator T const ();

> Is this legal overloading of operators?

No. All uses on non-const objects are ambiguous and all uses on const
objects are ill-formed. Try it.

Note that you again have two non-const functions. You can overload
on the constness of the object or the type of the operator, but not
on the constness of the type of the operator.

John

John Potter

unread,
Dec 21, 2003, 6:43:34 AM12/21/03
to
On 20 Dec 2003 20:26:15 -0500, "Dhruv" <dhru...@gmx.net> wrote:

> That's what I'm worried about. Because operator T const& is a const member
> function, it can be called on const As Well As non-const objects right? Or
> have I succeeded in confusing myself? So, I wanted some sort of guarantee
> that whenever I call operator conversion function on an object implicitly,
> the correct ones should be chosen. ie. For non-const objects, operator T&,
> and for const objects, operator T const&.

> Is there some sort of rule that if you have 2 identical functions
> overloaded by const qualification ONLY, then the const version will be
> called ONLY for Const objects?

I think you may still be confusing things.

operator T& (); // called only on non-const objects
operator T const& () const; // called only on const objects

It is the const on the end that makes the overload work. There is a
better match for the non-const function on non-const objects because
the other one requires a const qualification conversion. Yes, the
const function could be called on a non-const object, but not when
the non-const member is available.

If you remove the rightmost const or add one to the other function
then creation on an int& will use the operator int& and creation of
an int const& will be ambiguous.

John

Francis Glassborow

unread,
Dec 21, 2003, 11:32:23 AM12/21/03
to
In message <pan.2003.12.20....@gmx.net>, Dhruv
<dhru...@gmx.net> writes

>That's what I'm worried about. Because operator T const& is a const member
>function, it can be called on const As Well As non-const objects right? Or
>have I succeeded in confusing myself? So, I wanted some sort of guarantee
>that whenever I call operator conversion function on an object implicitly,
>the correct ones should be chosen. ie. For non-const objects, operator T&,
>and for const objects, operator T const&.

Given a pair of overloaded functions, one a const member and the other
an unqualified member the compiler is required to select the const
version for const objects (or const references) of the class type and
the unqualified version for non-const objects and references.

--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
or http://www.robinton.demon.co.uk

Dhruv

unread,
Dec 21, 2003, 12:49:38 PM12/21/03
to
On Sun, 21 Dec 2003 06:43:34 -0500, John Potter wrote:

> On 20 Dec 2003 20:26:15 -0500, "Dhruv" <dhru...@gmx.net> wrote:
>
> > That's what I'm worried about. Because operator T const& is a const member
> > function, it can be called on const As Well As non-const objects right? Or
> > have I succeeded in confusing myself? So, I wanted some sort of guarantee
> > that whenever I call operator conversion function on an object implicitly,
> > the correct ones should be chosen. ie. For non-const objects, operator T&,
> > and for const objects, operator T const&.
>
> > Is there some sort of rule that if you have 2 identical functions
> > overloaded by const qualification ONLY, then the const version will be
> > called ONLY for Const objects?
>
> I think you may still be confusing things.
>
> operator T& (); // called only on non-const objects
> operator T const& () const; // called only on const objects
>
> It is the const on the end that makes the overload work. There is a
> better match for the non-const function on non-const objects because
> the other one requires a const qualification conversion. Yes, the
> const function could be called on a non-const object, but not when
> the non-const member is available.

Ok, this was what I was getting confused about. I was thinking about the
case when there is no other function but the const qualified one, and then
I was trying to apply the same logic for when there is a non-const
identical funcion. Thanks ;-)

>
> If you remove the rightmost const or add one to the other function
> then creation on an int& will use the operator int& and creation of
> an int const& will be ambiguous.

The case you mention wherein the creation of an int fails whereas the
creation of an int const& fails is when both the functions are non-const
right?


Regards,
-Dhruv.

Dhruv

unread,
Dec 21, 2003, 12:50:23 PM12/21/03
to
On Sun, 21 Dec 2003 06:43:05 -0500, John Potter wrote:
[...]

> Note that you again have two non-const functions. You can overload
> on the constness of the object or the type of the operator, but not
> on the constness of the type of the operator.

Ok, got it! So, does this mechanism work on the tyopeof(this), meaning
that I can assume *this as being passed as a last parameter to the
function, and the signature of the function changing to:

struct class_name {
int& foo (); ___[1]
int const& foo () const; ___[2]

Would translate to:
int& foo (class_name& this);
int const& foo (class_name const& this);
};


Then, something like:
class_name n1;
class_name const n2;

n1.foo (); //would translate to: foo (n1); [1] is called.
and:
n2.foo (); //would translate to: foo (n2); [2] is called.

Hope I got that right?


Reagrds,
-Dhruv.

John Potter

unread,
Dec 22, 2003, 3:34:19 AM12/22/03
to
On 21 Dec 2003 12:49:38 -0500, "Dhruv" <dhru...@gmx.net> wrote:

> On Sun, 21 Dec 2003 06:43:34 -0500, John Potter wrote:

> > operator T& (); // called only on non-const objects
> > operator T const& () const; // called only on const objects

> > If you remove the rightmost const or add one to the other function


> > then creation on an int& will use the operator int& and creation of
> > an int const& will be ambiguous.

> The case you mention wherein the creation of an int fails whereas the
> creation of an int const& fails is when both the functions are non-const
> right?

When both functions are either non-const or const.

struct S {
operator int& () const { static int x(42); return x; }
operator int const& () const { static int const x(69); return x; }
};
void f (S const& s) {
int& ir = s; // uses int&
int const& icr = s; // ambiguous
}

In the first case, an int const& may not be used to initialize an
int& and there is only one choice. Seems to be overloading on return
type. In the second case, either will work and it is ambiguous.

John

Rani Sharoni

unread,
Dec 23, 2003, 12:00:37 PM12/23/03
to
Francis Glassborow <fra...@robinton.demon.co.uk> wrote in message >
> Given a pair of overloaded functions, one a const member and the other
> an unqualified member the compiler is required to select the const
> version for const objects (or const references) of the class type and
> the unqualified version for non-const objects and references.

Not in general:
struct A {
long* f(A const&) const;
char* f(A&);
};

A a;
long* p = a.f(A()); // fine

For overloading considerations a.f(A()) is exactly like A::f(a, A())
where *this is *directly* bounded to the first argument.

I'm sure that you meant for pair of overloaded functions that differ
only in the *this const qualifier.

Rani

Francis Glassborow

unread,
Dec 23, 2003, 3:53:39 PM12/23/03
to
In message <df893da6.03122...@posting.google.com>, Rani
Sharoni <rani_s...@hotmail.com> writes

>Not in general:
>struct A {
> long* f(A const&) const;
> char* f(A&);
>};
>
>A a;
>long* p = a.f(A()); // fine
>
>For overloading considerations a.f(A()) is exactly like A::f(a, A())
>where *this is *directly* bounded to the first argument.
>
>I'm sure that you meant for pair of overloaded functions that differ
>only in the *this const qualifier.

Yes, I wasn't intending to consider cases where the explicit parameters
were different.


--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
or http://www.robinton.demon.co.uk

Rani Sharoni

unread,
Dec 25, 2003, 4:28:35 PM12/25/03
to
John Potter <jpo...@falcon.lhup.edu> wrote in message
news:<qdv9uvkntcp0jm7ps...@4ax.com>...
This issue is a bit more complicated.

Consider the following case:
struct A1 {
operator char const*();
private: // investigation ;-)
operator char*();
};

char const* p1 = A1(); // #1

In this case there is no ambiguity and #1 is well-formed.
There are two conversion sequences (CS) to consider:
S1: A -> char* -> char const*
S2: A -> char const*

According to 13.3.3/1/6, A::operator const char* is better candidate
since the standard CS const char* (i.e. identity) is proper
subsequence of char* -> char const* (i.e. qualification adjustment)
and therefore is better per 13.3.3.2/3/1/2 (i.e. the proper
subsequence rule)

The original reference case is surprisingly different since according
to TC1 DR #59:
"Conversion functions that return "reference to cv2 X" return lvalues
of type "cv2 X" and are therefore considered to yield X for this
process of selecting candidate functions"

For example:
struct A2 {
operator char const&();
operator char&();
};

char const& r1 = A2(); // Ambiguous user-defined-conversion

Both conversion functions are viable and for ranking considerations
returns the same type (i.e. char).

There is an open DR (#233) about this inconsistency which seems to
violate the proper subsequence rule. The DR also mentioned the case
where inheritance is involved:
struct A {};
struct B : A {};

struct X {
operator A&();
private:
operator B&();
};

A& g = X(); // #2

The DR state that #2 is ambiguous yet all the compilers I tried think
that #2 is well-formed. Maybe the proper subsequence rule comes to
play in this case.

Now for the original quirky case:
struct A3 {
operator char&();
private:
operator char const&() const;
};

char const& r1 = A3(); // #3

The reason that #3 is well-formed is probably one of the most obscure
cases in the standard. There are two *viable* conversion sequences to
consider:
S1: A -> A& -> (UDC) char& -> char const&
S2: A -> A const& -> (UDC) char const&

>From the first look it seems that the proper subsequence rule doesn't
apply in this case since S1 is obviously not proper subsequence of S2
yet overloading thinks that S1 is better.
Similar case was presented one year ago in this NG
(http://tinyurl.com/502f) and I remember that Daveed Vandevoorde had
to ask Steve Adamczyk for the answer which means that the explanation
is far from being obvious (i.e known by few overload-resolution gurus
in the committee).

Eventually it boils down to 13.3.3.1/4 that yields that in the above
case the conversion is considered up to the UDC. This means that the
candidates CS are actually:
S1': A -> A&
S2': A -> A const&

S1' is obviously better than S2' per 13.3.3.2/3/1/4 which is related
to the proper subsequence rule.

>From the same reasoning the following is also well-formed:
struct A4 {
operator char*();
private:
operator void const*() const;
};

void const* p = A4(); // #5

After two months I realized that this obscure property (i.e.
13.3.3.1/4) can be exploited in order to implement one of the most
surprising type traits – is_base_and_derived that handles multiple and
private bases. http://tinyurl.com/2bpo2.

The final is_base_and_derived implementation contains fine explanation
by Terje Slettebo:
http://www.boost.org/boost/type_traits/is_base_and_derived.hpp

Regards,
Rani

0 new messages