Давайте посмотрим на это выражение:
f(**static_cast<A*>(NULL))
От самого внутреннего к самому внешнему, static_cast<A*>(NULL)
- это A*
(хотя предпочитают от nullptr
до NULL
, а также предпочитают использовать std::declval<A*>()
, чтобы получить "что-то типа A*
", а не старый подход приведения нулевого указателя).
Далее *(a prvalue of type A*)
дает вам lvalue типа A
. Вот что означает разыменование указателя, и оно не перегружается. Приятно, когда об этом легко рассуждать.
Далее *(an lvalue of type A)
. Чтобы выяснить, что это значит, мы преобразуем вызов функции-нотации, и наш набор кандидатов:
Первая пуля ничего не находит, A::operator*()
нет. Второй пункт, неквалифицированный поиск по operator*()
, найдет функцию C operator*(const C&);
, потому что она находится в области видимости. Это приемлемый кандидат, потому что A
конвертируется в C
через C(A const&)
. Третья пуля не имеет жизнеспособного кандидата.
Поскольку у нас есть только один жизнеспособный кандидат, это тривиально лучший жизнеспособный кандидат, поэтому *(lvalue of type A)
дает нам тип значения C
.
Чтобы конкретно ответить на ваш вопрос:
неявное преобразование происходит до фактической операции разыменования
Да, преобразование должно произойти, чтобы разрешить операцию разыменования. Хотя на самом деле это не «разыменование», это просто унарный operator*()
.
Наконец, f(prvalue of type C)
вызывает то, что у нас есть f
, которое дает нам double
.
<час />
Обратите внимание, что в современном C ++ я бы предложил написать чек как-то ближе к:
template <typename T>
struct t
: std::is_same<
decltype(f(*std::declval<T>())), // <== the type we get when we call f
// on a dereferenced T
double>
{ };