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?
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.
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.best.ics] explains which implicit conversion sequence is better, but it should be obvious that an identity conversion beats everything else.