Removing troublesom implicit conversions

155 views
Skip to first unread message

daryl.va...@maptek.com.au

unread,
Sep 24, 2014, 9:35:00 PM9/24/14
to std-pr...@isocpp.org
With refactoring work I have been doing over the past few months, I have come to the conclusion that the following implicit conversions are evil and shouldn't be allowed:
  • from numeric types to bool
  • from pointer types to bool
  • from "false" and "0" compile-time constant expressions to null pointers (I believe the particularly strange conversion of "false" to a null pointer is being fixed in an upcoming standard)

These conversions cause many bugs and refactoring pain with incorrect overloads getting called, etc. I can't think of one good reason why these conversions should be implicit, except for compatibility.

PLEASE consider deprecating these conversions in an upcoming revision of the C++ standard.

(And if compatibility with C is quoted as a reason for not doing so - then C should also deprecate these conversions. C should also adopt C++'s nullptr.)

Pablo Oliva

unread,
Sep 24, 2014, 9:40:39 PM9/24/14
to std-pr...@isocpp.org
Why wouldn't you ask your compiler manufacturer for a flag or an option to avoid implicit conversions, instead of modifying the whole standard?

There are many lines of code that rely on these conversions and what seems to be your goal (i.e., ease of debugging) can be achieved with just a compiler option.

Or can you elaborate on the notion of conversion evilness?

--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.

daryl.va...@maptek.com.au

unread,
Sep 24, 2014, 9:55:55 PM9/24/14
to std-pr...@isocpp.org
By evil I mean, say I have two overloads of a function:

void foo(int* IntPointer);
void foo(int IntValue);

... and I rename or remove the second overload.
Now, code the behaviour of this code silently changes:

foo(0);

... and there is no way of finding out where such changes have taken place.

I am not aware of any compiler that will warn in these cases. Unfortunately, the NULL constant is still widely used and relies on this behaviour.

I guess the idea of asking compiler vendors to offer a warning has merit. But I would also like to see the language itself move in the direction of more type safety.

If you want a null pointer, use "nullptr".
If you want to convert an integer to a bool, use " != 0".
If you want to convert a pointer to a bool, use " != nullptr".
If you want to convert a bool to an integer, use static_cast.

Seems simple to me, and I'm already in the habit of doing this for code I write.

daryl.va...@maptek.com.au

unread,
Sep 24, 2014, 10:05:51 PM9/24/14
to std-pr...@isocpp.org, daryl.va...@maptek.com.au
... and I forgot to specify that the conversion in the other direction, from bool to int, should also not be implicit.

Thiago Macieira

unread,
Sep 24, 2014, 10:45:46 PM9/24/14
to std-pr...@isocpp.org
On Wednesday 24 September 2014 18:35:00 daryl.va...@maptek.com.au wrote:
> (And if compatibility with C is quoted as a reason for not doing so - then
> C should also deprecate these conversions. C should also adopt C++'s
> nullptr.)

Two problems with that: first, the reason is probably compatibility with the
hundreds of millions of lines of code that expect that to work. It's extremely
common to write:

if (p)

with p being a pointer or an integer. Conversion of those to bool is required.

Conversion from literal zero to pointers is also required due to the millions
of lines of code that don't use nullptr yet.

The second problem is that C requires them for a different reason: booleans in
C are actually int, not the C99 type _Bool.

Anyway, as a QoI, your compiler should be able to warn of those uses,
especially the use of literal zero for a null pointer.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358

Myriachan

unread,
Sep 25, 2014, 3:46:46 AM9/25/14
to std-pr...@isocpp.org
On Wednesday, September 24, 2014 7:45:46 PM UTC-7, Thiago Macieira wrote:

On Wednesday, September 24, 2014 6:35:00 PM UTC-7, daryl.va...@maptek.com.au wrote:
(And if compatibility with C is quoted as a reason for not doing so - then C should also deprecate these conversions. C should also adopt C++'s nullptr.) 
 
Two problems with that: first, the reason is probably compatibility with the
hundreds of millions of lines of code that expect that to work. It's extremely
common to write:

        if (p)


I've always written C/C++ this way, and even get annoyed at how "!= null" is required in Java and C#.

As for C adopting C++'s nullptr, why?  With void * casting implicitly to any pointer type, C's NULL is semantically almost equivalent to C++'s nullptr already.

Anyway, as a QoI, your compiler should be able to warn of those uses,
especially the use of literal zero for a null pointer.


I will say, though, that it is a little silly that false and other integral literal expressions that evaluate to zero will implicitly convert to pointers:


#include <sys/socket.h>  // SHUT_WR is an enum whose value is 1

int main()
{
   
char *purr = false;
   
static_cast<void>(purr);
   
char *meow = ((4059079107ULL * 653022955U) & 4294967295U) - SHUT_WR;
   
static_cast<void>(meow);
   
return 0;
}


At least C++ doesn't allow the below nonsense, despite betraying the "literal" part of "user-defined literal":


#include <cstdint>

template <char...>
constexpr std::uint32_t operator "" _u32()
{
   
return UINT32_C(0);
}

int main()
{
   
char *meow = 0_u32;
   
static_cast<void>(meow);
   
return 0;
}

Melissa

Matthew Woehlke

unread,
Sep 25, 2014, 12:31:59 PM9/25/14
to std-pr...@isocpp.org
On 2014-09-24 21:35, daryl.va...@maptek.com.au wrote:
> With refactoring work I have been doing over the past few months, I have
> come to the conclusion that the following implicit conversions are evil and
> shouldn't be allowed:
>
> - from numeric types to bool
> - from pointer types to bool

I too am strongly opposed to breaking code like 'if(p)', 'if(!p)' (where
'p' is a pointer type). Hmm... and I'll extend that to numbers, also.

> - from "false" and "0" compile-time constant expressions to null
> pointers (I believe the particularly strange conversion of "false" to a
> null pointer is being fixed in an upcoming standard)

You'll break *MASSIVE* amounts of legacy code if '0' can no longer be
used as 'nullptr'.

That said, compilers can be encouraged to provide a warning for that
(and at least GCC already does). This should include the case you
mentioned of 'void foo(int*); foo(0);'. If not, I suggest you file a bug
report with the compiler.

Implicit conversion of things other than literal '0' to nullptr (and
I'll include '0U', '0L', 'static_cast<int>(0)', etc. in that) is the
only point I agree with. And that one seems to have general agreement
and, as you mention, will hopefully be fixed "soon".

--
Matthew

John Bytheway

unread,
Sep 25, 2014, 6:58:58 PM9/25/14
to std-pr...@isocpp.org
On 2014-09-25 03:46, Myriachan wrote:
> On Wednesday, September 24, 2014 7:45:46 PM UTC-7, Thiago Macieira wrote:
>
> On Wednesday, September 24, 2014 6:35:00 PM UTC-7,
> daryl.va...@maptek.com.au wrote:
>
> (And if compatibility with C is quoted as a reason for not doing
> so - then C should also deprecate these conversions. C should
> also adopt C++'s nullptr.)
>
>
>
> Two problems with that: first, the reason is probably compatibility
> with the
> hundreds of millions of lines of code that expect that to work. It's
> extremely
> common to write:
>
> if (p)
>
>
> I've always written C/C++ this way, and even get annoyed at how "!=
> null" is required in Java and C#.
>
> As for C adopting C++'s nullptr, why? With void * casting implicitly to
> any pointer type, C's NULL is semantically almost equivalent to C++'s
> nullptr already.

One reason would be supporting clean and safe code in headers used for
both languages.

John Bytheway

Richard Smith

unread,
Sep 25, 2014, 8:20:58 PM9/25/14
to std-pr...@isocpp.org
The C++ standard already allows the definition of NULL to be nullptr. Another way to achieve the same goal would be to start requiring that definition; then in code that you want to share between C and C++, use NULL.

Thiago Macieira

unread,
Sep 26, 2014, 12:45:34 AM9/26/14
to std-pr...@isocpp.org
On Thursday 25 September 2014 12:31:23 Matthew Woehlke wrote:
> Implicit conversion of things other than literal '0' to nullptr (and
> I'll include '0U', '0L', 'static_cast<int>(0)', etc. in that) is the
> only point I agree with. And that one seems to have general agreement
> and, as you mention, will hopefully be fixed "soon".

I've seen plenty of code using 0L as the null pointer, whether for stylistic
reasons (stands out) or because of an erroneous assumption that 0 wouldn't
work on 64-bit platforms. (That was old KDE code)

So, no literal zero whether unsigned, long or long long still still need to
cast to the null pointer for a while, albeit the compiler can produce a
warning.

Jim Porter

unread,
Sep 26, 2014, 3:12:40 AM9/26/14
to std-pr...@isocpp.org
On 9/24/2014 8:35 PM, daryl.va...@maptek.com.au wrote:
> With refactoring work I have been doing over the past few months, I have
> come to the conclusion that the following implicit conversions are evil
> and shouldn't be allowed:
>
> * from numeric types to bool
> * from pointer types to bool

I could see an argument for these being *explicit* conversions (pointer
types especially so), although I imagine that would still break a fair
amount of existing code. However, I agree with others that we absolutely
*shouldn't* break this:

int *p = something;
if(p) { /* ... */ }

A good first step would be to get compilers to optionally warn on
implicit conversion to bool from arithmetic or pointer types.

- Jim

Message has been deleted

daryl.va...@maptek.com.au

unread,
Sep 27, 2014, 1:55:34 AM9/27/14
to std-pr...@isocpp.org
It's interesting to note that shared_ptr has an explicit conversion operator for conversion to bool, specifically to avoid the problems that are encountered with raw pointers that are implicitly convertable to bool. Yet it is still possible to use a shared_ptr in an if condition like this:

shared_ptr ptr;
...
if (ptr) {...}

So, there's nothing to fear by making the conversion of raw pointers to bool explicit. The same would apply for conversion of integer types to bool.

I wasn't asking for these conversion operators to be removed completely, only for them to be made explicit. So it seems like we can come to some kind of agreement, at least on that issue.

If conversions of pointer and integer types to bool were made explicit, and conversion of bool to integer types were made explicit, then I would be happy.

As for conversion of "0", "0L" and "0UL" (but not false or other constant expressions) to null pointers, I'd be happy if C++ continues supporting that implicitly for the time being.

Thiago Macieira

unread,
Sep 27, 2014, 2:51:57 AM9/27/14
to std-pr...@isocpp.org
On Friday 26 September 2014 22:55:33 daryl.va...@maptek.com.au wrote:
> I wasn't asking for these conversion operators to be removed completely,
> only for them to be made explicit. So it seems like we can come to some
> kind of agreement, at least on that issue.

In other words, what did you exclude?

Peter Koch Larsen

unread,
Sep 27, 2014, 7:07:18 AM9/27/14
to std-pr...@isocpp.org
On Thu, Sep 25, 2014 at 3:55 AM, <daryl.va...@maptek.com.au> wrote:
> By evil I mean, say I have two overloads of a function:
>
> void foo(int* IntPointer);
> void foo(int IntValue);
>
> ... and I rename or remove the second overload.
> Now, code the behaviour of this code silently changes:
>
> foo(0);
>
> ... and there is no way of finding out where such changes have taken place.
>
[snip]

As others have said, this is not likely to happen - for many good reasons.
In hindsight, you might have saved yourself a lot of trouble by
deleting the function instead of removing it: void foo(int IntValue) =
delete;

/Peter

daryl.va...@maptek.com.au

unread,
Sep 28, 2014, 8:56:12 PM9/28/14
to std-pr...@isocpp.org
On Saturday, 27 September 2014 20:37:18 UTC+9:30, Peter Koch Larsen wrote:
As others have said, this is not likely to happen - for many good reasons.
In hindsight, you might have saved yourself a lot of trouble by
deleting the function instead of removing it: void foo(int IntValue) =
delete;

That would've worked, if I had a compiler that supported defaulted/deleted functions. But this problem doesn't just come up when deleting functions; it can come up when you least expect it. So I thought that more type safety in the language would go a long way towards preventing it, but obviously not everyone agrees, but I haven't yet seen many of those reasons why.
Reply all
Reply to author
Forward
0 new messages