Если они связаны
Давайте на мгновение предположим, что B
на самом деле является основанием D
. Тогда для вызова check
обе версии жизнеспособны, поскольку Host
можно преобразовать в D*
и B*
. Это определяемая пользователем последовательность преобразования, описанная 13.3.3.1.2
от Host<B, D>
до D*
и B*
соответственно. Для поиска функций преобразования, которые могут преобразовать класс, синтезируются следующие функции-кандидаты для первой функции check
в соответствии с 13.3.1.5/1
D* (Host<B, D>&)
Первая функция преобразования не подходит, потому что B*
нельзя преобразовать в D*
.
Для второй функции существуют следующие кандидаты:
B* (Host<B, D> const&)
D* (Host<B, D>&)
Это два кандидата в функции преобразования, которые принимают хост-объект. Первый берет его по константной ссылке, а второй нет. Таким образом, второй лучше соответствует неконстантному объекту *this
(подразумеваемый аргумент объекта) по 13.3.3.2/3b1sb4
и используется для преобразования в B*
для второй функции check
.
Если бы вы удалили константу, у нас были бы следующие кандидаты
B* (Host<B, D>&)
D* (Host<B, D>&)
Это означало бы, что мы больше не можем выбирать по константе. В обычном сценарии разрешения перегрузки вызов теперь будет неоднозначным, поскольку обычно возвращаемый тип не участвует в разрешении перегрузки. Однако для функций преобразования есть лазейка. Если две функции преобразования одинаково хороши, то тип их возвращаемого значения решает, какая из них лучше согласно 13.3.3/1
. Таким образом, если бы вы убрали константу, то была бы взята первая, потому что B*
лучше преобразуется в B*
, чем D*
в B*
.
Какая определенная пользователем последовательность преобразования лучше? Один для второй или первой функции проверки? Правило состоит в том, что определяемые пользователем последовательности преобразования можно сравнивать только в том случае, если они используют одну и ту же функцию преобразования или конструктор в соответствии с 13.3.3.2/3b2
. Здесь именно так: оба используют вторую функцию преобразования. Обратите внимание, что таким образом const важен, потому что он заставляет компилятор использовать вторую функцию преобразования.
Так как мы можем их сравнить - какой из них лучше? Правило состоит в том, что лучшее преобразование из типа, возвращаемого функцией преобразования, в тип назначения выигрывает (опять же на 13.3.3.2/3b2
). В этом случае D*
лучше конвертируется в D*
, чем в B*
. Таким образом выбирается первая функция и мы признаем наследство!
Обратите внимание: поскольку нам никогда не нужно было на самом деле преобразовывать в базовый класс, мы можем, таким образом, распознать частное наследование, потому что возможность преобразования из D*
в B*
не зависит от форма наследования по 4.10/3
Если они не родственники
Теперь предположим, что они не связаны по наследству. Таким образом, для первой функции у нас есть следующие кандидаты
D* (Host<B, D>&)
А для второго у нас теперь другой набор
B* (Host<B, D> const&)
Поскольку мы не можем преобразовать D*
в B*
, если у нас нет отношения наследования, у нас теперь нет общей функции преобразования среди двух определяемых пользователем последовательностей преобразования! Таким образом, мы были бы неоднозначны, если бы не тот факт, что первая функция является шаблоном. Шаблоны — это второй вариант, когда есть функция, не являющаяся шаблоном, которая одинаково хороша в соответствии с 13.3.3/1
. Таким образом, мы выбираем нешаблонную функцию (вторую) и признаем, что между B
и D
нет наследования!
person
Johannes Schaub - litb
schedule
26.05.2010
static yes check<int>(D*, T=int);
лучше, чемstatic no check(B*, int);
? - person MSalters   schedule 26.05.2010operator B*()
было константой? - person Alexey Malistov   schedule 26.05.2010is_base_of
: ideone.com/T0C1V Она не работает со старыми версиями GCC. версии, хотя (GCC4.3 работает нормально). - person Johannes Schaub - litb   schedule 26.05.2010static yes check(B*, int);
, но в коде есть толькоstatic no check(B*, int);
. - person MSalters   schedule 26.05.2010template
аргументами префиксT_
;-) - person underscore_d   schedule 27.02.2016is_base_of<Base,Base>::value
должно бытьtrue
; это возвращаетfalse
. - person ST0   schedule 01.06.2016is_base_of<A,A>
, эта реализация также неправильно вычисляетis_base_of<void,A>
! - person Quuxplusone   schedule 20.05.2017__is_base_of(T,U)
— это встроенный компилятор, также известный как магия. Это не часть языка C++; это присуще GCC. Единственная причина, по которой он существует (насколько мне известно), состоит в том, чтобы ускорить оценку этого свойства типа, что (если вы не используете встроенную функцию magic) требует большого количества экземпляров шаблона и разрешения перегрузки. Таких магических встроенных функций гораздо больше, например__is_union
,__is_trivially_constructible
и__is_final
. - person Quuxplusone   schedule 20.05.2017