Как устранить неоднозначность вызова перегруженной функции с литералом 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) Есть ли проблема с этим следующим образом, и какова причина того, что это работает?
Трюк с шаблоном работает, потому что правила для разрешения вызовов перегруженных функций и шаблонных версий разработаны так, чтобы отдавать предпочтение версиям перегруженных функций без шаблонов.