explicit conversion

  • Thread starter Tanmoy Bhattacharya
  • Start date
T

Tanmoy Bhattacharya

Hi,

This is a question about whether I am right that a particular syntactic
sugar is missing in C++.

Let me explain with an example. Let us say I have a class for complex
numbers, and I want people to be able to initialize real numbers (like
an object of type double) from it ... in such a context (or if someone
explicitly casts to double), the imaginary part is to be ignored.
However, I certainly do not want people to be able to ask whether
Complex(1,2) > 0, or even whether Complex(1,0)>0, or pass a complex
number to a function expecting a real number. Of course, it is easy to
do that by forcing people to write definitions like `double x =
z.realpart();', but I am asking about letting people use the syntax
`double x(z)', or `double x = z'

If instead of double, I had a class Double, I could play convoluted
games with `explicit Double(Complex::Double);' and Complex::Double an
otherwise unused class (possibly by naming it
IWillBreakYourBonesIfYouUseIt) to which class Complex can be converted.
But is it really not possible to do it if I want double instead of
Double. (The rule against multiple implicit user-defined conversions and
no constructors for non-class objects seems to defeat me. If only the
rule were against more than two rather than more than one ...)

More fundamentally, is there a reason (implementation difficulty,
difficulty of specification, neatness) because of which `explicit
operator' is not in the language? Constructors and conversions work
very similarly most of the time, so why this asymmetry?

Many thanks
Tanmoy
 
V

Victor Bazarov

Tanmoy said:

Hey, it's been a while, hasn't it? Welcome back!
This is a question about whether I am right that a particular syntactic
sugar is missing in C++.
[...]
More fundamentally, is there a reason (implementation difficulty,
difficulty of specification, neatness) because of which `explicit
operator' is not in the language? Constructors and conversions work
very similarly most of the time, so why this asymmetry?

Actually, I recall recently seeing some discussion about this in either
comp.lang.c++.moderated or in comp.std.c++ (or both). I think the answer
to this is that it's not going to help you, IIUIC.

You specifically don't want

Complex a(1,2);
if (a > 0) ...

to be legal but you do want

Complex a(1,2);
double r(a);

to be legal. But there is no difference between the two. 'a' is either
going to be converted to 'double' when a 'double' is expected or not.

If you provide a conversion operator, making it explicit will force you
to write, e.g.,

Complex a(1,2);
double r(double(a));

but you already have that ability by calling '.realpart()'.

Making constructors explicit forces somebody to write 'Type(arg)' instead
of letting the compiler figure out that 'Type' should be constructed from
the 'arg'. So, making 'Type::eek:perator Type2()' to be explicit does not
miraculously allow _some_ conversions while disallowing other.

That's my take on it, anyway.

V
 

তন্ময় ভট্টাচার্&

Hey, it's been a while, hasn't it? Welcome back!

Yeah ... got a bit too busy with things.
You specifically don't want

Complex a(1,2);
if (a > 0) ...

to be legal but you do want

Complex a(1,2);
double r(a);

to be legal. But there is no difference between the two. 'a' is either
going to be converted to 'double' when a 'double' is expected or not.
Correct.

Making constructors explicit forces somebody to write 'Type(arg)' instead
of letting the compiler figure out that 'Type' should be constructed from
the 'arg'.

And allows you to write Type x(arg), though not Type x = arg.
So, making 'Type::eek:perator Type2()' to be explicit does not
miraculously allow _some_ conversions while disallowing other.

The semantics I desire is for explicit Type::eek:perator Type2() (or
explicit operator ::Type2(Type) if such were to be allowed instead) to
behave identically to explicit Type2::Type2(Type).

Thanks (and thanks for the hint of previous discussion... I googled
some from 1996 as well!)
Tanmoy
 
V

Victor Bazarov

"তন্ময়
ভট্টাচার্য্য"
(Parts snipped without indication)
[...]
Making constructors explicit forces somebody to write 'Type(arg)' instead
of letting the compiler figure out that 'Type' should be constructed from
the 'arg'.

And allows you to write Type x(arg), though not Type x = arg.
Right.
So, making 'Type::eek:perator Type2()' to be explicit does not
miraculously allow _some_ conversions while disallowing other.

The semantics I desire is for explicit Type::eek:perator Type2() (or
explicit operator ::Type2(Type) if such were to be allowed instead) to
behave identically to explicit Type2::Type2(Type).


I am not sure I follow. This is how it is now:

struct Type {};
struct Type2 {
explicit Type2(Type);
};

Type arg;
Type2 x(arg);
Type2 y = arg; // error -- no conversion exists

So, now replacing the constructor in Type2 with the cast in Type

struct Type2 {};
struct Type {
explicit operator Type2();
};

should allow

Type arg;
Type2 x(arg); // OK
Type2 y = arg; // error

, right? What I don't understand is how the conversion operator's being
explicit would affect the construction of Type2. Essentially, the two
forms

Type2 x(arg);

and

Type2 y = arg;

are using the same constructor, Type2(Type2 const&), provided by the
compiler, where the reference is bound to a temporary object created from
'arg'. There is no other constructor to use, is there? So, they both
are like

Type2 const& rtemp(arg.operator Type2());
Type2 z(rtemp);

and if we forego the creation of the temporary (as allowed), they both
are equivalent to

Type2 z(arg.operator Type2());

How are you going to make the compiler distinguish between 'Type2 x(arg)'
and 'Type2 x = arg', when the [other part of the ] language says that they
are the same? Conclusion: introduction of 'explicit' type conversion ops
will require other fundamental changes to the language, which will most
likely affect _tons_ of existing programs. Besides, it's unclear that such
change _can_ be done.

..Shrug.

I hope you find plenty of more logical explanations in the other threads
on Google.

Victor
 
T

tanmoy

Victor said:
How are you going to make the compiler distinguish between 'Type2 x(arg)'
and 'Type2 x = arg', when the [other part of the ] language says that they
are the same? Conclusion: introduction of 'explicit' type conversion ops
will require other fundamental changes to the language, which will most
likely affect _tons_ of existing programs. Besides, it's unclear that such
change _can_ be done.

Thanks for your analysis. I, however, have difficulty following your
argument in detail. It is not quite correct that Type2 x(arg) and
Type2 x=arg are identical: the former calls the explicit constructor
for Type2::Type2(Type) if it exists, whereas the latter cannot call
that. In other words, yes, both are supposed to call
Type2::Type2(Type) if it exists, but the difference is whether an
explicit Type2::Type2(Type) participates in the resolution.

Similarly, it would be nice if when Type2::Type2(Type) did not exist
(to avoid ambiguity), but an `explicit' version of Type::eek:perator
Type2() existed, it would participate in resolution only in the former,
and not the latter, case. Again, though in both cases the copy
constructor is invoked as Type2::Type2(arg.Type::eek:perator Type2()), the
question is only whether declarations of Type::eek:perator Type2 that are
marked `explicit' should participate.

Note that the compiler is supposed to known the semantics of the
(impicit) copy constructor, and it is typically elided in this case, so
it is difficult to argue that it is a hardship to remember the direct
initialization context in this case.

I do not see how this affects any existing code that is correct. An
example would help. I am not suggesting that something other than the
copy constructor be used, nor that something other than the conversion
operator be used: only suppress the use of the conversion under certain
conditions.

But maybe given the extensive discussion of the whole issue in
comp.std.c++ in 1996, still available on google, we should take the
discussion off-line. Incidentally, that discussion was mainly
concerned with using static_cast<double>, with which you probably would
have no quarrel, rather than direct initialization of double, though
this was briefly mentioned as well.

Tanmoy
 
V

Victor Bazarov

[...]
I do not see how this affects any existing code that is correct. An
example would help. I am not suggesting that something other than the
copy constructor be used, nor that something other than the conversion
operator be used: only suppress the use of the conversion under certain
conditions.

You'd need to state those conditions. I presented this example:

class Type2 {};
class Type {
operator Type2();
};

Type arg;
Type2 x(arg); // 1
Type2 x = arg; // 2

Explain what certain conditions apply here to allow suppression of
Type::eek:perator Type2 used in the case (2), if you think this might
be beneficial.
But maybe given the extensive discussion of the whole issue in
comp.std.c++ in 1996, still available on google, we should take the
discussion off-line. Incidentally, that discussion was mainly
concerned with using static_cast<double>, with which you probably would
have no quarrel, rather than direct initialization of double, though
this was briefly mentioned as well.

Well, I'd have to study the 1996 discussion to be able to participate
in any even off-line conversation (if you'd like to continue). I only
have one comment: it seems that you yourself noted that it would really
be a true syntactic sugar (given it's possible to implement), and it's
not like a serious problem that can be solved by making operators
explicit has been overlooked. Perhaps the absence of the real need
is what holds the introduction back...

V
 

তন্ময় ভট্টাচার্&

Victor Bazarov said:
You'd need to state those conditions. I presented this example:

class Type2 {};
class Type {
operator Type2();
};

Type arg;
Type2 x(arg); // 1
Type2 x = arg; // 2

Explain what certain conditions apply here to allow suppression of
Type::eek:perator Type2 used in the case (2), if you think this might
be beneficial.

I thought I did exactly that in my previous post: the proposed rule
would be that a Type::eek:perator Type2() declared as explicit will be
used only in two cases: (1) when Type appears as the only parameter of
an explicit constructor or direct initialization, and (2) when a
static or C-style cast operator is used. In the first of these, the
copy constructor of Type2 must be accessible, though it can be elided.
It is specifically not used when the conversions called for are
implicit *and* are not being used in a direct initialization. Note
that // 2 in your code is an initialization, but not direct
initialization, but // 1 is.

To complete the symmetry, one could also have that an explicit
constructor be useable in (2) in this paragraph. (i.e. Not only
Type2(Type), but also (Type2)Type would be able to use an explicit
constuctor for Type2): this is, however, a change which might
theoretically affect some programs, though I doubt
it occurs too often.

It turns out that the second of the two is more important, especially
for templates. The idea is that if I have a function which works on
say the string representation of its parameter, it is tempting to
write `template <T> f(T x) { String y(x); /* use y */}' or that String
y(x) bit is written as `String y = static_cast<String>(x)'. Note that
this avoids namespace pollution: the template writer does not force a
particular name like toString which must appear for people to be using
the template: it must only support the initialization or the static
cast. And, in fact, this is precisely where explicit constructors are
useful: an explicit constructor for String(T) is all that is needed to
make this work. Unfortunately, this needs access to the String class
definition, which users should not necessarily have; and, moreover, it
is impossible if a non-class type like double is substituted for
string in this example.

The point is that the need for `explicit' in the constructor is
orthogonal to the access requirements to the internals of the class,
and even to the interface of the class. Ergo, `explicit', if it
exists, should not need a change to the class definition as it
currently does.
Well, I'd have to study the 1996 discussion to be able to participate
in any even off-line conversation (if you'd like to continue). I only
have one comment: it seems that you yourself noted that it would really
be a true syntactic sugar (given it's possible to implement), and it's
not like a serious problem that can be solved by making operators
explicit has been overlooked. Perhaps the absence of the real need
is what holds the introduction back...

It was a nice discussion a long time back. It turns out that, as
explained variously and repeatedly on the thread, the original
proposal was to have both explicit constructors and explicit
conversion operators, though I did not find the originally intended
semantics mentioned on the thread (it certainly would have allowed the
static cast<Type2>, and disallowed calling a non-constructor function
with parameter Type2). This was the intent of the proposer, as well
as some of the committee members. The actual text of the proposal,
however, did not mention the operators, and that is how some else
interpreted it. The difference came to light only after the standard
became pretty final, and it was not considered worth changing it.

So, you are right that the `absence of the real need is what holds the
introduction back', but there does exist, and did exist, a perceived
need all along.

In any case, thanks for pointing me to the discussion. And, now I
will go back to my slumber till I learn how to google well.

Tanmoy
 
P

puppet_sock

(e-mail address removed) (তন্ময় ভট্টাচার্য্য) wrote in message [content snipped]

But, seriously dude. Why would you copy/paste your handle from a
Microsoft product? Did you need to spell check it?

Anyway, in order to avoid that cruft, you do the following if you
*must* edit in Word or whatever. Copy/paste it into notepad first.
That will cause all the "mystery" formatting to be dropped in
favour of plain old ASCII text. Then copy it to your news reader.
Socks
 
V

Victor Bazarov

(e-mail address removed) (তন্ময় ভট্টাচার্য্য) wrote in message [content snipped]

But, seriously dude. Why would you copy/paste your handle from a
Microsoft product? Did you need to spell check it?

Anyway, in order to avoid that cruft, you do the following if you
*must* edit in Word or whatever. Copy/paste it into notepad first.
That will cause all the "mystery" formatting to be dropped in
favour of plain old ASCII text. Then copy it to your news reader.

There is no ASCII text in whatever lies between the parentheses.
It's Bengali. And it's in Netscape's version of UNICODE, I guess.

V
 

তন্ময় ভট্টাচার্&

(e-mail address removed) (তন্ময় ভট্টাচার্য্য) wrote in message news:<[email protected]>...
confusing my name with microsoft formatting. At least noone thought
it was an APL program!

Seriously, news and web are two different media, and even Google
should have known that. It used html entities to represent unicode
characters, instead of mime-encoding in utf-8 or something. After
this post, I promise to spelll my name in a more conventional fashion.

Thanks for pointing it out to me
Tanmoy
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,072
Latest member
trafficcone

Latest Threads

Top