C++ Logo


Advanced search

Re: Implicit conversion sequence from literal 0

From: Edward Catmur <ecatmur_at_[hidden]>
Date: Mon, 9 Jan 2023 01:33:43 +0000
On Mon, 9 Jan 2023 at 00:13, Phil <std-discussion_at_[hidden]> wrote:

> Are you saying that an implicit conversion sequence from int to void* does
> not exist? Or that it does exist but is not viable in this case?
> As far as I know, there can’t be no non-existing conversion sequence, but
> a sequence can happen to be empty, i.e. without any conversions.
> That being said, there is specified an „implicit null pointer conversion“
> from a „null pointer constant“, which is either an „integer literal with
> value 0“ or a „prvalue of type std::nullptr_t“.
> If the evaluated argument is not a „null pointer constant“, there is no „implicit
> null pointer conversion“ and therefore it could happen that the conversion
> sequence for that argument is empty and therefore unable to convert.
> According to [over.match.viable]/4, a function F is only considered
> viable, if (despite other requirements) there exists for each argument an
> implicit conversion sequence **that converts that argument to the
> corresponding parameter of F**.
> So, given `foo(1, 1)`, there is no implicit conversion sequence that
> converts literal 1 to void* -> that overload is a candidate but not even
> viable at all.

That reads to me the same as saying that an implicit conversion sequence
from literal 1 to void* does not exist.

Thus an implicit conversion sequence from int to void* does exist when that
int is literal 0, but not otherwise. Is that correct?

If so, we have a conflict with the original mentioned paragraph of

at what point in the process is [conv.ptr]/1 considered?
> At least by [over.match.viable] and [over.match.best] (and generally by
> many others regarding different topics).

We are specifically discussing [over.best.ics.general], so
[over.match.best] is the process you should be referencing.

> I'm fine with that. What follows from that ambiguity?
> The call is ill-formed.

Not quite. It forms an ambiguous conversion sequence, which has a
detectable difference to an ill-formed call; for example, it is better than
an ellipsis conversion sequence:

struct U { short x : 1; };
template<class T, bool B>
struct S {
    struct X {
    int f(X);
    int f(...) requires (B);
    struct Y {
    int g(Y);
    int g(...) requires (B);
template<class T, bool B>
concept C = requires(S<T, B> s) { s.f(0); };
template<class T, bool B>
concept D = requires(S<T, B> s) { s.f(1); };
template<class T, bool B>
concept E = requires(S<T, B> s, U u) { s.g(u.x); };
int ans[] = {
    C<short, false>, C<short, true>,
    D<short, false>, D<short, true>,
    E<short, false>, E<short, true>,

Received on 2023-01-09 01:33:57