Почему нет необходимости специализироваться на `std::nullptr_t`, если есть специализированная функция И шаблонная функция

Рассмотрим следующий код:

#include <iostream>
using namespace std;

void fun(const char* s){
    if (s == nullptr) {
        puts("const char* nullptr");
    } else {
        printf("%s\n", s);
    }
}

template <typename T>
void fun(T* p){
    printf("%p\n", p);
}

int main() {
    int a;
    fun("abc"); // Resolves to fun(const char*)
    fun(&a); // Specializes the template to int*
    fun(nullptr); // Uses fun(const char*)??
    fun(NULL); // Same as above
}

Я удивлен, что g++ 7.2.0 не выдает ошибку о неоднозначном разрешении перегрузки, как мне кажется nullptr а также NULL может вписаться в любой тип указателя, в том числе fun(int*) специализированный из шаблона, при условии, что нет перегрузки, специализированной для std::nullptr_t,

Почему fun(nullptr) а также fun(NULL)решает непосредственно fun(const char *)?

1 ответ

Решение

std::nullptr_t не указатель, поэтому он не будет соответствовать шаблону T* в шаблоне вашей функции.

Как бы нелогично это ни было, следующее утверждение не сработает:

static_assert(std::is_pointer<std::nullptr_t>() == false);

Что касается NULL, это определенный макросом реализации. Если верить cppreference, это либо целочисленный литерал со значением ноль (поэтому не указатель), либо значение типа std::nullptr_t, что объясняется выше. Это не void* указатель.

Другие вопросы по тегам