Альтернативная реализация is_base_of (проверка базовых / производных отношений) - PullRequest
3 голосов
/ 24 апреля 2011

У меня был хороший вопрос о том, как is_base_of реализован в boost (который решает, является ли данный class базой другого class во время компиляции).

Увидев такой код в первый раз, я удивился тому, как можно заставить вещи работать так хорошо! Тем не менее, многие шаги я запутался, чтобы понять (после прочтения всех ответов). Итак, мне было интересно, если эта функциональность может быть реализована альтернативно. Я попробовал следующее:

template<class B, class D>
struct is_base_of
{
  template<typename T> struct dummy {};
  struct Child : D, dummy<int> {};

  static B* Check (B*);
  template<class T> static char Check (dummy<T>*);

  static const bool value = (sizeof(Check((Child*)0)) == sizeof(B*));
};

Работает нормально, как и ожидалось в общем случае.

Единственная проблема для private/protected наследования. Он идет и выбирает ожидаемую функцию, но также показывает ошибку как: - is an inaccessible base of .... Я был бы признателен, если бы кто-то мог предложить какие-то небольшие изменения, отсутствующие в коде, для решения этой проблемы (Если не функциональность, то хотя бы избавиться от сообщения об ошибке).

[Примечание: я предположил, что char и B* всегда будут иметь разные размеры и избегать типовых типизированных «да» и «нет»]

1 Ответ

2 голосов
/ 24 апреля 2011

Я думаю, что ключевой частью связанного решения было то, что независимо от того, каким будет конечный результат (связанный или не связанный) - выбранная последовательность преобразования фактически не будет включать наследуемость, для которой проверяется. Компилятор учитывает это в процессе выбора соответствующей версии функции check. Однако каждый раз в конце выбранный путь фактически не будет его использовать.

Однако в вашем коде, если классы связаны, вызов check действительно использует наследование, преобразованное из Child* в B*. И это - я боюсь, это не может быть легко исправлено, поскольку подход, который вы представили, сильно отличается.

Если связано

Ваше решение

Child равно D, следовательно, также B. Поэтому существует преобразование из Child* в B*. Поэтому первая версия Check жизнеспособна. Вторая версия Check является жизнеспособной, так как Child также dummy<int> и, следовательно, Child* - dummy<int>*. Первая версия выбрана, поскольку она не требует специализации параметров шаблона.

Связанное решение

Для первой версии check: Host<B, D> преобразуется через преобразование, определенное пользователем, в D*. Тип результата преобразования в точности соответствует аргументу функции. Наследование здесь не используется.

Для второй версии check: Host<B, D> снова преобразуется через пользовательское преобразование в D*. Тип результата преобразования - D*, который может быть далее преобразован в B* для соответствия аргументу функции. Наследование здесь действительно используется.

Однако в конце компилятор выбирает первую версию, поскольку было отмечено, что результат преобразования лучше соответствует аргументу check.

Если не связано

Ваше решение

Child не B, поэтому первая версия Check не является опцией. Вторая версия жизнеспособна, так как Child по-прежнему dummy<int>, поэтому может быть специализированным с dummy<T>. Поскольку есть только один вариант, выбор тривиален.

Связанное решение

Для первой версии check: Host<B, D> преобразуется в D* путем преобразования, определенного пользователем.

Для второй версии check: Host<B, D> преобразуется в const Host<B, D>, а затем в B* путем преобразования, определенного пользователем.

Пути были бы несопоставимы, если бы не тот факт, что только одна версия функции check имеет аргументы шаблона.

Как видите, два подхода значительно различаются.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...