Why does overload resolution prefer std::nullptr_t over a class when passing {}?
Asked Answered
N

1

17

The following code prints nullptr instead of empty (godbolt link):

#include <iostream>

class empty { };

#if 1
void f(std::nullptr_t) {
    std::cout << "nullptr\n";
}
#endif

void f(empty) {
    std::cout << "empty\n";
}

int main() {
    f({});
}

Disabling the f(nullptr_t) variant causes empty to be printed. What are the rules C++ is using to select the nullptr_t variant over the empty variant when both are available?

Neocolonialism answered 20/12, 2023 at 16:31 Comment(9)
See overload_resolutionOrjonikidze
Why did you expect the empty overload? I'd expect an ambiguity.Foursquare
Why would the empty overload be selected? If it wasn't specified (as it seems to be), it would be ambiguous.Stormy
@Orjonikidze Anything specific on that page I should look out for? I took a look at it and cannot interpret what, specifically, is causing the compiler to prefer one overload over the other.Neocolonialism
@Foursquare there is no ambiguity. If both are present, the compiler selects the nullptr_t variant.Neocolonialism
"What are the rules C++ is using" - if you want to know that, then read the standard - it is all public.Scolopendrid
I know, I was suprised that you expected the empty overload to be called.Foursquare
@JesperJuhl I think we ordinary mortals sometimes need help with that.Unmanly
@JesperJuhl The standard proper is not freely available; it's fairly pricey (~240 US$ at time of comment). The working drafts are freely available, however, and cppreference is even better for most use cases.Riane
T
32

Initializing std::nullptr_t (or any other fundamental type) with {} is better because it result in an identity conversion, whereas initializing class types results in a user-defined conversion sequence:

Otherwise, if the parameter has an aggregate type which can be initialized from the initializer list according to the rules for aggregate initialization ([dcl.init.aggr]), the implicit conversion sequence is a user-defined conversion sequence whose second standard conversion sequence is an identity conversion.

- [over.ics.list] p8

empty is an aggregate type, so this paragraph applies. std::nullptr_t is not a class, so the following paragraph applies:

Otherwise, if the parameter type is not a class:

  • (10.1) [...]
  • (10.2) if the initializer list has no elements, the implicit conversion sequence is the identity conversion.
  • [...]

- [over.ics.list] p10

[over.best.ics] explains which implicit conversion sequence is better, but it should be obvious that an identity conversion beats everything else.

Tripalmitin answered 20/12, 2023 at 16:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.