Я думаю, что есть несколько взаимодействующих проблем, способствующих ошибке:
- типы указателей на члены имеют неинтуитивные характеристики преобразования типов
- объявление использования не влияет на тип имени, введенного в область действия
- в то время как имя
base_der::print
доступно, класс base
все еще не существует, и в попытке преобразовать указатель на член фактический тип класса в типе указатель на член является частью рассмотрения.
C ++ 03 7.3.3 «Декларация об использовании»
Объявление использования вводит имя в декларативную область, в которой появляется объявление использования. Это имя является синонимом имени некоторой сущности, объявленной в другом месте.
Обратите внимание, что хотя имя заносится в новый «регион», это синоним - тип того, к чему относится имя, одинаков. Итак, я думаю, что в вашем примере имя base_der::print
имеет тип void (base::*)(int)
, а не тип void (base_der::*)(int)
.
Стандарт C ++ 03 также говорит о преобразованиях между типами указатель на элемент ( 4.11 "Преобразование указателя в элемент" ):
Значение типа "указатель на член B типа cv T", где B является типом класса, может быть преобразовано в значение типа "указатель на член D типа cv T", где D является производный класс (пункт 10) из B. Если B является недоступным (пункт 11), неоднозначным (10.2) или виртуальным (10.1) базовым классом D, программа, которая требует этого преобразования, является неадекватной. Результат преобразования ссылается на тот же элемент, что и указатель на член до преобразования, но ссылается на элемент базового класса, как если бы он был членом производного класса. Результат ссылается на член в экземпляре D из B. Поскольку результат имеет тип «указатель на член D типа cv T», его можно разыменовать с помощью объекта D. Результат такой же, как если бы указатель на член B был разыменован с подобъектом B в D.
Также примечание 7.3.3 / 13 «Объявление использования» (выделение добавлено):
В целях разрешения перегрузки функции, которые вводятся посредством объявления об использовании в производный класс, будут обрабатываться так, как если бы они были членами производного класса. В частности, неявный параметр this должен рассматриваться как указатель на производный класс, а не на базовый класс. Это не влияет на тип функции, и во всех других отношениях функция остается членом базового класса.
Теперь пример кода, который генерирует ошибку:
// This doesn't:
void (base_der::* print)(int);
print = &base_der::print; // Compile error here
пытается преобразовать «указатель на член D» в «указатель на член B», что является преобразованием в неправильном направлении. Если вы подумаете об этом на мгновение, вы поймете, почему преобразование в этом направлении небезопасно. Переменная типа «указатель на член B» может не использоваться с объектом, который имеет какое-либо отношение к class D
- но если вы вызываете функцию с типом «указатель на член D» (что и является void (base_der::* print)(int)
is), вполне вероятно, что указатель this
будет указывать на объект D
.
В любом случае, хотя я думаю, что корень проблемы - это проблема преобразования, я думаю, что вы получаете жалобу на доступность, потому что когда компилятор пытается обработать преобразование, он сначала проверяет доступность base
- и хотя имя base_der::print
(которое является псевдонимом для base::print
) доступно из-за объявления using
, класс base
все еще не является.
Отказ от ответственности: этот анализ исходит от кого-то, кто имеет небольшой опыт в нюансах типов указатель на элемент. Это область C ++, которая сложна, трудна в использовании, кроме как в самых простых сценариях, и, по-видимому, имеет много проблем с переносимостью (см. Статью Дуга Клагстона, http://www.codeproject.com/KB/cpp/FastDelegate.aspx,, которая достаточно стара для решения этих проблем возможно, уже решены, но я подозреваю, что нет).
И когда вы говорите, что что-то в C ++ является одной из более сложных или менее понятных областей, это говорит о многом.