Как разрешить неоднозначность вызова перегруженной функции с помощью литерала 0 и указателя

Я уверен, что это, должно быть, уже было здесь, но я не нашел много информации о том, как решить эту проблему (без кастования вызова):

Учитывая две перегрузки, я хочу, чтобы вызов с функцией с литералом 0 всегда вызывал неподписанную версию int:

void func( unsigned int ) {
    cout << "unsigned int" << endl;
}

void func( void * ) {
    cout << "void *" << endl;
}

func( 0 ); // error: ambiguous call

Я понимаю, почему это происходит, но я не хочу писать func (0u) или даже func (static_cast (0)) все время. Поэтому мои вопросы:

1) Есть ли рекомендуемый способ сделать это в целом?

2) Есть ли какие-либо проблемы с этим следующим образом и в чем причина этого?

void func( unsigned int ) {
    cout << "unsigned int" << endl;
}

template <typename T>
void func( T * ) {
    static_assert( std::is_same<T, void>::value, "only void pointers allowed" );
    cout << "void *" << endl;
}

func( 0 ); // calls func( unsigned int )!

Ответ 1

1) Есть ли рекомендуемый способ сделать это в целом?

Да, я сделал бы это так, как вы делали в 2). Я не думаю, что есть более глубокое значение, почему 2) работает. Тип int просто не соответствует T*, поэтому он не может найти T. Он будет игнорировать шаблон.

Ответ 2

Что вы делаете в 2) работает и, вероятно, лучший способ это сделать.

В ситуациях, когда вы не хотите изменять функции, вы можете сделать явный приведение, чтобы дать компилятору подсказку:

func((void *) 0);
func((unsigned int) 0);

Ответ 3

Я предлагаю вам посмотреть нулевой указатель из С++ 0x (см. this). Он определяет класс, представляющий нулевые указатели любого типа. Пример, который вы только что дали, был на самом деле одним из мотивов включения nullptr_t (класса)/nullptr (значение) в С++ 0x. Фактически это позволяет вам устранить этот вызов, поставив 0, когда захочет беззнаковая версия int, и nullptr, если вы хотите другой.

Вы можете просто реализовать этот трюк в небольшом классе утилиты, пока ваш компилятор его не поддержит (или просто используйте его, если ваш компилятор реализует эту часть следующего стандарта).

Ответ 4

1) Есть ли рекомендуемый способ сделать это в целом?

Проблема заключается в том, что буква 0 является int, а не unsigned int, и есть допустимые преобразования от int до unsigned int и от int до void*. Я не могу сказать, что есть рекомендуемый способ справиться с этой проблемой. Помимо способов, которые вы уже нашли, вы также можете добавить еще одну перегрузку:

void func(int i) 
{
    assert(i >= 0);
    return func(static_cast<unsigned int>(i));
}

2) Есть ли какие-либо проблемы с этим следующим образом и в чем причина этого?

Шаблонный трюк работает, потому что правила для разрешения вызовов перегруженных функций и шаблонных версий предназначены для предпочтения не templated версий перегруженных функций.