c++gccclangoverload-resolutionnullptr

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


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?


Solution

  • 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.