Проблема рефакторинга с любопытно повторяющимся шаблоном - PullRequest
3 голосов
/ 24 января 2012

Следующий код не может скомпилироваться на g ++ 4.6.1:

template<class Base>
struct GetBase {
  Base * getBase() {
    return static_cast<Base *>(this);
  }
};

template<class Derived>
struct Parent : private GetBase<Derived> {
  using GetBase<Derived>::getBase;
  int y() {
    return getBase()->x();
  }
};

struct Child : public Parent<Child> {
  int x() {
    return 5;
  }
  int z() {
    return y();
  }
};

с ошибкой

In member function ‘Base* GetBase<Base>::getBase() [with Base = Child]’:
    instantiated from ‘int Parent<Derived>::y() [with Derived = Child]’
    instantiated from here
error: ‘GetBase<Child>’ is an inaccessible base of ‘Child’

Изменение static_cast на reinterpret_cast приведет к скомпилированному коду и егобудет работать в этом случае, но мне интересно, является ли это приемлемым решением во всех случаях?т. е. всегда ли указатель на базовый класс отличается от этого?Я предполагаю, что с множественным наследованием это может произойти, если у родителей есть члены данных?Если GetBase - первый суперкласс, гарантированно ли равны указатели this?

Ответы [ 2 ]

3 голосов
/ 24 января 2012

Мне интересно, является ли это приемлемым решением во всех случаях?

Нет. (см. Ниже)

когда-нибудь случается, что указатель на базовый класс не совпадает с этим?

Да.

  • С несколькиминаследование, нельзя ожидать, что базовые классы будут иметь один и тот же адрес.

  • В зависимости от компилятора, производный класс с указателем vtable может не иметь тот же this, что и базовыйкласс, который не имеет указателя vtable.

При явном преобразовании в базу static_cast является соответствующим C ++ приведением.

1 голос
/ 24 января 2012

хороший вопрос;это заставило меня узнать что-то новое о static_cast.

. Я думаю, что следующий код достигает того, что вы хотите: предоставляет базовый класс для CRTP с функцией-членом, которая преобразует this в тип Derived, но толькоделает его доступным для прямых потомков этого базового класса.Он компилируется с GCC 4.3.4 ( проверено на ideone ) и Clang (протестировано на llvm.org).Извините, я не смог устоять перед изменением имен, которые меня смущают.

#include <iostream>

template<class Derived>
class CRTP {
protected:
  Derived * derived_this() {
    return static_cast<Derived *>(this);
  }
};

template<class Derived>
struct Parent : public CRTP<Derived> {
private:
  using CRTP<Derived>::derived_this;
public:
  int y() {
    return derived_this()->x();
  }
};

struct Child : public Parent<Child> {
  int x() {
    return 5;
  }
  int z() {
    return y();
  }
};

int main() {
  std::cout << Child().z() << std::endl;
  return 0;
}

Этот вариант работает, потому что наследование является публичным, что позволяет стандартное преобразование указателя на производный класс вуказатель на базовый класс, а также обратное преобразование (из базового в производное) с static_cast, в котором нуждается CRTP.Частное наследство в вашем коде запрещено.Поэтому я сделал публичное наследование, изменил защищаемый метод и в Parent дополнительно ограничил доступ, поместив объявление using в приватный раздел.

...