Использование функций друзей для извлечения информации о типах из полиморфного класса? - PullRequest
0 голосов
/ 04 февраля 2019

У меня есть вопрос об использовании функций-друзей внутри вложенного полиморфного класса для получения информации о типах.

У меня есть следующий код, который демонстрирует, что я делаю.Учитывая два класса, A<T> и B<T>, я могу создать полиморфную оболочку во время выполнения, которая содержит A или B.На практике эта оболочка может содержать все, что угодно, в том числе другой шаблонный класс с аналогичным статическим интерфейсом.

template<typename T>
struct A {
  T value_;

  A(T value) : value_(value) {}

  void sayHello() const {
    std::cout << "Hello from A! " << value_ << '\n';
  }
};

template<typename T>
struct B {
  T value_;

  B(T value) : value_(value) {}

  void sayHello() const {
    std::cout << "Hello from B! " << value_ << '\n';
  }
};

Оболочка основана на концепциях полиморфизма времени выполнения Шона Родителя, но мне нужно получить информацию о типе длянекоторые операции.Например, может быть, я могу добавить A и B, но не A и C.По сути, если я помещаю функцию Friend в шаблонный класс-оболочку, я могу привести объект обратно к его исходному типу.

class Wrapper {
private:
  class Concept {
  public:
    virtual ~Concept() = default;

    virtual void sayHello() const = 0;
  };

  template<typename T>
  class Model final
      : public Concept {
  private:
    T data_;

  public:
    Model(T data) : data_(data) {}

    virtual void sayHello() const override {
      data_.sayHello();
    }

  private:
    template<typename U>
    friend inline void doSomething(const Concept &lhs, const B<U> &rhs) {
      T x = static_cast<const Model<T> &>(lhs).data_;

      x.sayHello();
      rhs.sayHello();

      auto y = x.value_ + rhs.value_;

      std::cout << y << '\n';
    }
  };

  template<typename U>
  friend inline void doSomething(const Concept &lhs, const B<U> &rhs);

  std::shared_ptr<const Concept> ptr_;

public:
  template<typename T>
  explicit inline Wrapper(T a)
      : ptr_(std::make_shared<Model<A<T>>>(std::move(a))) {}

  template<typename U>
  friend inline void someFriend(Wrapper &lhs, B<U> &rhs) {
    doSomething(*lhs.ptr_, rhs);
  }
};

Обратите внимание, что я могу static_cast класс Concept вфункция Friend, потому что ее тип может быть выведен из контекста класса Model<T>.

Так что я могу использовать такой код:

Wrapper a(1);
B<int> b{2};

someFriend(a, b);

Какие выходные данные:

Hello from A! 1
Hello from B! 2
3

Мой вопрос заключается в том, есть ли какая-то непредвиденная проблема, связанная с такими действиями.Кроме того, если я заменю объект, удерживаемый в указателе, static_cast все еще будет работать?

Некоторые предварительные тесты, которые я провел, показывают, что это довольно надежно, но я иногда сталкиваюсь с проблемой, что вызов, кажется, «специализируется» вокруг первого объекта и затем не переключается, если указатель меняется нановый объект.

Здесь - ссылка на код.

1 Ответ

0 голосов
/ 04 февраля 2019

Вы нарушаете ODR (одно правило определения), и, следовательно, ваш код неверен, диагностика не требуется.

В частности:

template<typename U>
friend inline void doSomething(const Concept &lhs, const B<U> &rhs);

существует более одного определенияэтого;на самом деле, есть один на Model<T>.Согласно ODR, это должно быть одно определение.

Плохо сформирован, диагностика не требуется (IL-NDR) - это худшее, что вы можете сделать на земле стандартов C ++.

СимптомВы видите, что вызывается какая-то случайная реализация, и "залипает".Ничто совершенно не безопасно.

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