Different in the Revised Language Definition?
Speaking of that grotty feel
to the language, having to write op_Implicit to
specify a conversion just didn’t feel like C++ in the T1 language design. For example,
here is a definition of MyDouble taken
from the T1 language specification:
__gc struct MyDouble
op_Implicit( int i );
static int op_Explicit(
MyDouble* val );
op_Explicit( MyDouble* val );
This says that, given an integer, the algorithm for
converting that integer into a MyDouble is
provided by the op_Implicit operator.
Moreover, that conversion will be carried out implicitly by the compiler. Similarly,
given a MyDouble object,
the two op_Explicit operators
provide the respective algorithms for converting that object into either an integer
or a managed String entity. However, the compiler will not be carried out the conversion
unless explicitly requested by the user.
In C#, this would look as follows:
static implicit operator MyDouble( int i
static explicit operator int( MyDouble val
static explicit operator string( MyDouble
And apart from the weirdness of the explicit public access
label for each member, the C# code looks a lot more like C++ than the Managed Extensions to C++ does.
When I was writing C# code, I could usually guess at the meaning of a construct without
going to the book and that tickled my C++ basic instincts. When I write T1 code, I
can never figure things out [it seems] without cracking open the book, or asking one
of the experts here. Not only does that make me feel dumb, but it breaks my coding
`zone’, and I don’t feel as good about what I’m doing [that is, my code], which is
not good if I spend a good part of my day coding.
So we had to fix that. But how should we do that?
On one hand, C++ programmers are left slightly reeling
by the absence of a single argument constructor being construed as a conversion operator.
On the other hand, however, that design proved grotty enough to manage that the ISO
committee introduced a keyword, explicit,
just to reign in its unintended consequences –
for example, an Array class which takes a single integer argument as a dimension will
implicitly convert any integer into an Array object even when that is the very last
thing one wants. Andy Koenig was the first person who brought that to my attention
when he explained a design idiom of a dummy second argument to a constructor just
to prevent such a bad thing from happening. So I don’t regret at all the absence of
a single constructor implicit conversion semantic for C++/CLI.
On the other hand, it is not ever a good idea to
provide a conversion pair when designing a class type within C++. The best example
for that is the standard string class. The implicit conversion is the single-argument
constructor taking a C-style string. However, it does not provide the corresponding
implicit conversion operator – that of converting a string object to a C-style string,
but rather requires the user to explicitly invoke a named function – in this case, c_str().
So, associating an implicit/explicit behavior on
a conversion operator [as well as encapsulating the set of conversions to a single
form of declaration] seems an improvement on the original C++ support for conversion
operators, which has been a public cautionary tale since 1988 when Robert Murray gave
a Usenix C++ talk entitled Building
Well-Behaved Type Relationships in C++ and which led to an explicit keyword.
The revised T2 language support for conversion operators looks as follows:
ref struct MyDouble
static operator MyDouble^
( int i );
static explicit operator int (
MyDouble^ val );
static explicit operator String^
( MyDouble^ val );
where the default behavior for the operator is to
support an implicit application of the conversion algorithm, freeing us from having
to introduce yet another contextual keyword (yack!).
disclaimer: This posting is
provided “AS IS” with no warranties, and confers no rights.