Topic: explicit conversion operators


Author: kanze@gabi-soft.fr (J. Kanze)
Date: 1996/09/18
Raw View
clamage@taumet.Eng.Sun.COM (Steve Clamage) writes:

> If you want a "tiny integer", you can use unsigned char or signed
> char, but don't expect to mix pointers to them freely with each
> other or with "char*". If you want to deal with actual characters,
> use type char (or wchar_t) exclusively.

Except, of course, when using the ctype.h functions to test whether the
actual char isalpha, isdigit, etc.  If the char type is signed, as it is
on many machines, it may contain values which result in undefined
behavior when passed as an argument to one of these functions.  Although
you are guaranteed that all of the characters in the basic character set
will be represented as a positive value in a char (even if it is
signed), other values may occur because of additional characters not in
the basic character set (accented characters, principally, in in theory,
at least, even $), user input error (entering the name of a binary file
when ASCII is expected), or a previous programming error (forgetting to
validate all input).

--
James Kanze           (+33) 88 14 49 00          email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs Bourgeois, 67000 Strasbourg, France
Conseils en informatique industrielle --
                            -- Beratung in industrieller Datenverarbeitung


[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: kanze@lts.sel.alcatel.de (James Kanze US/ESC 60/3/141 #40763)
Date: 1996/09/11
Raw View
In article <199609062232.PAA02833@taumet.eng.sun.com>
clamage@taumet.Eng.Sun.COM (Steve Clamage) writes:

|> In article 21AF@mindspring.com, "Donley R. P'Simer" <donley@mindspring.com> writes:
|> >Scott Johnson wrote:
|> >
|> >To add my own twist to the discussion, I will say that I find it
|> >annoying that this code produces errors when warnings would be
|> >sufficient:

Ask your implementor.  In your example program, the compiler is required
by the standard to issue a diagnostic, because the code is not C++.
Once the diagnostic is issued, it is free to go on and generate any
conversions it wants.

I suspect that the reason this is an error, and not a warning, is that
the majority of programmers do *NOT* want the compiler to generate code
if the source is not legal C++.  (On the other hand, a number of
implementations have extensions turned on by default, so who knows.)

|> >void print(char*);
|> >int main()
|> >{
|> >   unsigned char* psz = "Hello, World!";// error, cannot convert!
|> >   print(psz);    // error, cannot convert!
|> >}
|> >
|> >The error on the declaration of 'psz' is the most annoying of all.
|> >Why can't the compiler implicitly cast a constant?

|> Because the types "char", "unsigned char", and "signed char" are
|> three different types, even though some of them will share the
|> same reperesentation. A literal string has type "array of char",
|> which is converted to "char*" in an expression. A "char*" and
|> "unsigned char*" are incompatible, even if "char" and
|> "unsigned char" have the same representation. Similarly, "int"
|> and "short" might have the same representation, but "int*" and
|> "short*" are incompatible regardless.

|> If you want a "tiny integer", you can use unsigned char or signed
|> char, but don't expect to mix pointers to them freely with each
|> other or with "char*". If you want to deal with actual characters,
|> use type char (or wchar_t) exclusively.

Except, of course, when using functions defined in ctype.h, since they
are not defined for all values of char.  (This is a historical problem.
If you use use_facet< ctype< char > >( locale::global ).is, this should
not be a problem.)
--
James Kanze         Tel.: (+33) 88 14 49 00        email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
Conseils,    tudes et r   alisations en logiciel orient    objet --
                -- A la recherche d'une activit    dans une region francophone
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: kanze@lts.sel.alcatel.de (James Kanze US/ESC 60/3/141 #40763)
Date: 1996/09/11
Raw View
In article <32302767.5E486488@master.grad.hr> Kresimir Fresl
<fresl@grad.hr> writes:
|> James Kanze wrote:

|> > ...  Conversion of a C style
|> > string to a 'string' object is implicit (because of the corresponding
|> > constructor), conversion in the other direction requires the use of the
|> > explicit function string::c_str.

|> Why is this so? As far as I can remember, this question occured few
|> times in this newsgroup (and, maybe, in c.l.c++ and/or c.l.c++.moderated),
|> but I never found a satisfying answer.

There are two aspects:

1. Both the implicit conversion and the explicit function (c_str) are
dangerous in the sense that they return a pointer to part of the
internal representation.  This pointer may be invalidated by any
non-const function of the object, including the destructor.

2. A conversion operator can be called implicitly, without appearing in
the code.  It is thus extremely easy for the conversion operator to be
called unknowingly by the programmer.  The explicit function cannot be
called unknowingly, but must be explicitly requested by the programmer.

Thus, for example, some current code contains the following:

 char const*   f() ;

 char const*   p = f() ;

This is perfectly legal, and works.  As part of a policy of continual
improvement, however, the declaration of f() (in a header file, of
course) is changed to:

 string    f() ;

I think that most people would agree that this is a reasonable
evolution.  Note, however, that the return value of f is a temporary,
which will be destructed at the end of the enclosing full expression.
So what happens with the above code:

1. If there is an implicit conversion operator string::operator char
const*(), the above code is syntactically correct, and compiles without
error.  At run-time, p contains an invalid pointer (undefined behavior).
It is not hard to imagine implementations/scenarios where the code will
still work "most of the time."  The resulting error can be extremely
difficult to track down.

2. If there is no implicit conversion operator, the compiler will
generate an error.  The programmer is required to adapt his code so that
it will work.  Obviously, in this case, just calling c_str will *NOT*
make the code work.  Depending on other constraints (schedule, etc.), he
will either rework his code completely, in order to use string, or he
will declare a temporary with the same lifetime as p, assign the results
of f to that, and then call c_str on that temporary.  Either way, the
resulting program is correct, and does not invoke undefined behavior.
--
James Kanze         Tel.: (+33) 88 14 49 00        email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
Conseils,    tudes et r   alisations en logiciel orient    objet --
                -- A la recherche d'une activit    dans une region francophone
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: geurtswe@imec.be (Werner Geurts)
Date: 1996/09/03
Raw View
According to the DWP $7.1.2

"The explicit specifier shall be used only in declarations of construc-
  tors within a class declaration;"

The explicit specifier can thus not be used for conversion operators.
What is the reason for this asymetry?

I would like to be able to write the following:

class num {
public:
   num();
   num(int);
   explicit operator int();
};

num operator+(num,num);

void foo()
{
   num n;
   n = n + 1;      // must call operator+(num,num), not the built-in one
     // integer literals in num expressions are common
   ...
   int A[10];
   i = A[int(n)];  // num subscripts are exceptional, so explicit cast is ok.
}

I do not want to make the num(int) constructor explicit, because then I have
to cast all the integer literals in num expressions.

------------------------------------------
Werner Geurts
IMEC, Kapeldreef 75, B-3001 Leuven, Belgium
PHONE :  +32/16/281258
E-MAIL : geurts@imec.be
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: clamage@taumet.eng.sun.com (Steve Clamage)
Date: 1996/09/04
Raw View
In article AA16940@imec.be,  geurtswe@imec.be (Werner Geurts) writes:
>According to the DWP $7.1.2
>
>"The explicit specifier shall be used only in declarations of construc-
>  tors within a class declaration;"
>
>The explicit specifier can thus not be used for conversion operators.
>What is the reason for this asymetry?
>
>I would like to be able to write the following:
>
>class num {
>public:
>   num();
>   num(int);
>   explicit operator int();
>};

The proposal to add "explicit" constructors did not mention type
conversion operators, and no one (that I can recall) suggested
including them. I assumed at the time that omitting type
conversion operators was deliberate.

If you want to require an explicit cast to perform a conversion,
your could create a named function instead of an operator. For
example, your int conversion function could be called to_int,
and instead of writing
 static_cast<int>(foo)
you would write
 foo.to_int()

---
Steve Clamage, stephen.clamage@eng.sun.com




[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: kanze@lts.sel.alcatel.de (James Kanze US/ESC 60/3/141 #40763)
Date: 1996/09/04
Raw View
In article <199609021509.AA16940@imec.be> geurtswe@imec.be (Werner
Geurts) writes:

|> According to the DWP $7.1.2

|> "The explicit specifier shall be used only in declarations of construc-
|>   tors within a class declaration;"

|> The explicit specifier can thus not be used for conversion operators.
|> What is the reason for this asymetry?

Probably because there is already a way of making the conversions
implicit: use a const function.

|> I would like to be able to write the following:

|> class num {
|> public:
|>    num();
|>    num(int);
|>    explicit operator int();
|> };

What would be the problem with using "int num::asInt() const" instead of
the conversion operator.

|> num operator+(num,num);

|> void foo()
|> {
|>    num n;
|>    n = n + 1;      // must call operator+(num,num), not the built-in one
|>      // integer literals in num expressions are common
|>    ...
|>    int A[10];
|>    i = A[int(n)];  // num subscripts are exceptional, so explicit cast is ok.

Would "i = A[ n.asInt() ]" not be OK?  Why not?

|> }

|> I do not want to make the num(int) constructor explicit, because then I have
|> to cast all the integer literals in num expressions.

The case pretty much parallels that of strings.  Conversion of a C style
string to a 'string' object is implicit (because of the corresponding
constructor), conversion in the other direction requires the use of the
explicit function string::c_str.
--
James Kanze         Tel.: (+33) 88 14 49 00        email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
Conseils,    tudes et r   alisations en logiciel orient    objet --
                -- A la recherche d'une activit    dans une region francophone



[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: "Bradd W. Szonye" <bradds@ix.netcom.com>
Date: 1996/09/04
Raw View
Werner Geurts <geurtswe@imec.be> wrote in article
<199609021509.AA16940@imec.be>...
> According to the DWP $7.1.2
>
> "The explicit specifier shall be used only in declarations of construc-
>   tors within a class declaration;"
>
> The explicit specifier can thus not be used for conversion operators.
> What is the reason for this asymetry?
>
> I would like to be able to write the following:
>
> class num {
> public:
>    num();
>    num(int);
>    explicit operator int();
> };
>
> num operator+(num,num);
>
> void foo()
> {
>    num n;
>    n = n + 1;      // must call operator+(num,num), not the built-in one
>      // integer literals in num expressions are common
>    ...
>    int A[10];
>    i = A[int(n)];  // num subscripts are exceptional, so explicit cast is
ok.
> }

There are at least three alternatives to what you want here.

You can define a member function int value() or int explicit_int() which is
a simple accessor for the integer value. Therefore, your array declaration
becomes: i = A[n.value()].

If you prefer a more "natural" syntax, you could write a global friend of
num called "int explicit_int(num)" or even a template function "int
explicitcast<int>(num)." The array declaration then becomes: i =
A[explicit_int(num)].

Finally, you can write operator+ to deal with integers better, *and* allow
implicit conversion to int:

num operator+(num, num);
num operator+(int, num);
num operator+(num, int);

... where the last two functions forward the operator to operator+(num,
num). When you do this, you have an exact match for (n + m), (n + 1), or (1
+ n), which takes precedence over a user-defined conversion. Even integral
promotions and standard conversions (short to int, long to int) will use
your custom operator over the standard one, so that (n + 1L) still calls
operator+(num, int). Plus, you can write "i = A[n]" without the cast.

Constructors need the explicit prefix because there is a semantic
difference between "num n(1);" and "num n; n.set_value(1);" but there is no
semantic difference between operator int() and int value(), only a
different name. I do think that explicit conversion operators are
convenient, but they are not necessary in the way that explicit
constructors are. The standards committee tends to avoid semantic sugar
over things that the language already allows conveniently enough.
--
Bradd W. Szonye
bradds@ix.netcom.com
http://www.netcom.com/~bradds


[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: sj@aracnet.com (Scott Johnson)
Date: 1996/09/04
Raw View
In article <199609021509.AA16940@imec.be>,
Werner Geurts <geurtswe@imec.be> wrote:
>According to the DWP $7.1.2
>
>"The explicit specifier shall be used only in declarations of construc-
>  tors within a class declaration;"
>
>The explicit specifier can thus not be used for conversion operators.
>What is the reason for this asymetry?

While we are at it....it would be kinda nice to be able to declare
function parameters as explicit, too.....

i.e.

void foo (double);
void bar (explicit double);

int main (void)
{
    int i=4;

    foo(i); // legal; int is cast to double
    bar(i); // illegal--no implicit cast allowed
    bar( static_cast<double>(i) ); // legal; cast is explicit.

    return 0;
}

Would be useful for implementing functions which require a specific
type of parameter and no other.

How this would deal with integral promotions (shor to int, etc.) needs to
be hashed out.  (As I understand it, these are dealt with differently than
casts.)  I would suggest explicit bar these, too...unless an explicit cast
is given.

Has this been suggested before?


Scott


--
/--------------------------------------------------------------------------\
|Scott Johnson -- Professional (sometimes) SW Engineer and all-purpose Geek|
|I don't speak for nobody but myself, which everyone else is thankful for  |
\--------------------------------------------------------------------------/
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: "Donley R. P'Simer" <donley@mindspring.com>
Date: 1996/09/06
Raw View
Scott Johnson wrote:
>
> While we are at it....it would be kinda nice to be able to declare
> function parameters as explicit, too.....
>
> i.e.
>
> void foo (double);
> void bar (explicit double);
>
> int main (void)
> {
>     int i=4;
>
>     foo(i);     // legal; int is cast to double
>     bar(i);     // illegal--no implicit cast allowed
>     bar( static_cast<double>(i) ); // legal; cast is explicit.
>
>     return 0;
> }
>
> Would be useful for implementing functions which require a specific
> type of parameter and no other.
>
In the current state of the language the function is guaranteed to
receive a value of the type specified in the signature. If implicit
conversion is possible then explicit conversion is not required.
Why would you be concerned about whether or not this conversion
takes place explicitly or implicitly? Can you give a practical
application of this?

To add my own twist to the discussion, I will say that I find it
annoying that this code produces errors when warnings would be
sufficient:

void print(char*);
int main()
{
   unsigned char* psz = "Hello, World!";// error, cannot convert!
   print(psz);    // error, cannot convert!
}

The error on the declaration of 'psz' is the most annoying of all.
Why can't the compiler implicitly cast a constant? If char's were
unsigned by default (a compiler flag on most compilers), this would
not be a problem, but of course, switching 'psz' to signed would
produce exactly the same errors.

 Donley
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Kresimir Fresl <fresl@grad.hr>
Date: 1996/09/06
Raw View
James Kanze wrote:

> ...  Conversion of a C style
> string to a 'string' object is implicit (because of the corresponding
> constructor), conversion in the other direction requires the use of the
> explicit function string::c_str.

Why is this so? As far as I can remember, this question occured few
times in this newsgroup (and, maybe, in c.l.c++ and/or c.l.c++.moderated),
but I never found a satisfying answer.

In fact, few months ago I posted a question in which I made hasty
statement:

``I agree that implicit conversion is (almost always) dangerous,
and I do not propose to add conversion operator to string class.''

which provoked Andrew Bell to ask:

``Is there an explanation for why implicit conversion (at least to
char*) is dangerous?  It doesn't seem to me that requiring the member
function call is that much safer, although admittedly it is easier to
grep for.''

There wasn't any reply; and, in this particular case (string to char*)
I couldn't find any example. Somebody?


My reason for (re)opening this issue is my original question,
concerning file streams and strings/char*'s:

I thought (and I still think) that it is annoying that one have to
write, for example:

     cout << "Enter file name -> ";
     string file_name;
     cin >> file_name;
     ifstream (file_name.c_str());
               ^^^^^^^^^^^^^^^^^

instead of just:

     ifstream (file_name);

Of course, one can declare `file_name' as `char[]' of `char*',
but then he must `manualy' reserve storage space for it, and
file name lengths are very different on different operating systems.

And, from the theoretical point of view, streams and strings are
`high level abstractions', while char*'s are `(very) low level'.
Standard library force us to mix them (at least in previous
example).

In original post I proposed to add to file stream classes
constructors with string parameters for file names. But, conversion
operator can solve this problem as well; as Andrew Bell said:
``However, the added complexity of code w/o a conversion operator
seems like it would add almost as many problems.''

Any opinions??


Kresimir Fresl

Faculty of Civil Engineering, Zagreb, Croatia
fresl@master.grad.hr
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: clamage@taumet.Eng.Sun.COM (Steve Clamage)
Date: 1996/09/07
Raw View
In article 21AF@mindspring.com, "Donley R. P'Simer" <donley@mindspring.com> writes:
>Scott Johnson wrote:
>
>To add my own twist to the discussion, I will say that I find it
>annoying that this code produces errors when warnings would be
>sufficient:
>
>void print(char*);
>int main()
>{
>   unsigned char* psz = "Hello, World!";// error, cannot convert!
>   print(psz);    // error, cannot convert!
>}
>
>The error on the declaration of 'psz' is the most annoying of all.
>Why can't the compiler implicitly cast a constant?

Because the types "char", "unsigned char", and "signed char" are
three different types, even though some of them will share the
same reperesentation. A literal string has type "array of char",
which is converted to "char*" in an expression. A "char*" and
"unsigned char*" are incompatible, even if "char" and
"unsigned char" have the same representation. Similarly, "int"
and "short" might have the same representation, but "int*" and
"short*" are incompatible regardless.

If you want a "tiny integer", you can use unsigned char or signed
char, but don't expect to mix pointers to them freely with each
other or with "char*". If you want to deal with actual characters,
use type char (or wchar_t) exclusively.
---
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]