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

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

Учитывая две перегрузки, я хочу, чтобы вызов с функцией с литералом 0 всегда вызывал версию unsigned 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 )!

4 ответа

Решение

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

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

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

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

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

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

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

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) Есть ли проблема с этим следующим образом, и какова причина того, что это работает?

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

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