С++, преобразование bool всегда возвращается к неявному преобразованию в void *?
Вопрос: Не подразумеваемые конверсии bool всегда возвращаются к попытке неявного преобразования в void*
? (Если такая функция преобразования существует для типа). Если да, то почему?
Рассмотрим следующую короткую программу:
#include <iostream>
class Foo{
public:
operator void*() const
{
std::cout << "operator void*() const" << std::endl;
return 0;
}
};
int main()
{
Foo f;
if(f)
std::cout << "True" << std::endl;
else
std::cout << "False" << std::endl;
return 0;
}
Выход этой программы:
operator void*() const
False
была вызвана функция преобразования в void*
.
Если мы помечаем квалификатор explicit
перед функцией преобразования, то неявное преобразование в void*
завершится с ошибкой.
Edit:
Кажется, много ответов заключается в том, что "нулевые указатели могут быть преобразованы в false
". Я понимаю это, мой вопрос касался "если я не могу напрямую вызвать operator bool()
, тогда я попробую преобразование в любой указатель".
Ответы
Ответ 1
Если компилятор не может напрямую преобразовать определяемый пользователем тип в bool
, он пытается сделать это косвенно, то есть преобразовать (посредством пользовательского преобразования) в тип, который может быть преобразован в bool
без привлечения другое пользовательское преобразование. Список таких типов включает (и, по-видимому, ограничен) следующие типы:
- целочисленный арифметический тип (
char
, int
и т.д.)
- арифметический тип с плавающей запятой (
float
, double
, long double
)
- тип указателя (
void*
принадлежит здесь, но он также может быть const std::vector<Something>*
)
- указатель на функцию (включая указатель на функцию-член)
- ссылочный тип для любого из вышеперечисленных
Обратите внимание, однако, что должно существовать только одно такое косвенное преобразование. Если возможны два или более преобразования из приведенного выше списка, компилятор столкнется с двусмысленностью и сообщит об ошибке.
Ответ 2
Для нескольких ссылок в стандарте:
Ответ 3
Что действительно происходит, так это то, что ваш класс имеет неявное преобразование в тип указателя void*
в этом случае. Вы возвращаете 0
, который является макросом NULL, который принимается как тип указателя.
Указатели имеют неявное преобразование в booleans, а нулевые указатели преобразуются в false.
Действительно, у вас может быть другое неявное преобразование в указатель для Foo
:
operator int*() const
{
std::cout << "operator int* const" << std::endl;
return new int(3);
}
И ваш выход изменится на
оператор int * const
True
Однако, если у вас оба, вы получите ошибку компилятора:
class Foo{
public:
operator int*() const
{
std::cout << "operator int* const" << std::endl;
return new int(3);
}
operator void*() const
{
std::cout << "operator void*() const" << std::endl;
return 0;
}
};
main.cpp: 26: 9: ошибка: преобразование из 'Foo' в 'bool' неоднозначно
Если, однако, вы явно определяете преобразование, тоже bool, то оно не является двусмысленным:
operator void*() const
{
std::cout << "operator void*() const" << std::endl;
return 0;
}
operator bool() const
{
std::cout << "operator bool() const" << std::endl;
return true;
} // <--- compiler chooses this one
Тема неявных преобразований действительно довольно интересна, поскольку она отражает то, как компилятор выбирает подходящую функцию-член для заданных аргументов (конверсии значений, интегральные рекламные акции и т.д.).
То есть, у компилятора есть список приоритетов, который он выберет, пытаясь определить, что вы имеете в виду. Если две перегрузки имеют одинаковый приоритет, вы получите сообщение об ошибке.
Например, operator bool
всегда будет выбран, но если вам пришлось выбирать из operator int
и operator void*
, тогда будет выбрано operator int
, потому что он выбирает числовое преобразование по конверсиям указателей.
Однако, если у вас были как operator int
, так и operator char
, вы получили бы ошибку, потому что это как числовые интегральные преобразования.
Ответ 4
Любое интегральное преобразование operator
будет работать одинаково. Вы возвращаете 0
в свой оператор, поэтому False
.
operator [int,double,uint64_t,<any_integral>]() const
{
std::cout << "integral operator called" << std::endl;
return 0;
}
В логическом выражении может использоваться любой интегральный тип.
Ответ 5
Это может быть любой тип, который может использоваться в булевом контексте, а void *
здесь не является особенным, eh?