Виртуальное наследование и статическое наследование - смешение в C ++ - PullRequest
9 голосов
/ 19 ноября 2010

Если у вас есть что-то вроде этого:

#include <iostream>

template<typename T> class A
{
public:
    void func()
    {
        T::func();
    }
};

class B : public A<B>
{
public:
    virtual void func()
    {
        std::cout << "into func";
    }
};

class C : public B
{
};

int main()
{
  C c;
  c.func();

  return 0;
}

Функция func () отправляется динамически?
Как вы могли бы реализовать класс A таким образом, чтобы, если у B было виртуальное переопределение, что оно отправлялось динамически, но статически отправлялось, если B нет?

Редактировать: мой код не скомпилирован? Извините ребята. Я сейчас немного болен. Мой новый код также не компилируется, но это часть вопроса. Кроме того, этот вопрос для меня, а не для часто задаваемых вопросов.

#include <iostream>

template<typename T> class A
{
public:
    void func()
    {
        T::func();
    }
};

class B : public A<B>
{
public:
    virtual void func()
    {
        std::cout << "in B::func()\n";
    }
};

class C : public B
{
public:
    virtual void func() {
        std::cout << "in C::func()\n";
    }
};
class D : public A<D> {
    void func() {
        std::cout << "in D::func()\n";
    }
};
class E : public D {
    void func() {
        std::cout << "in E::func()\n";
    }
};

int main()
{
  C c;
  c.func();
  A<B>& ref = c;
  ref.func(); // Invokes dynamic lookup, as B declared itself virtual
  A<D>* ptr = new E;
  ptr->func(); // Calls D::func statically as D did not declare itself virtual
  std::cin.get();

  return 0;
}

visual studio 2010\projects\temp\temp\main.cpp(8): error C2352: 'B::func' : illegal call of non-static member function
      visual studio 2010\projects\temp\temp\main.cpp(15) : see declaration of 'B::func'
      visual studio 2010\projects\temp\temp\main.cpp(7) : while compiling class template member function 'void A<T>::func(void)'
      with
      [
          T=B
      ]
      visual studio 2010\projects\temp\temp\main.cpp(13) : see reference to class template instantiation 'A<T>' being compiled
      with
      [
          T=B
      ]

Ответы [ 6 ]

2 голосов
/ 19 ноября 2010

Я не уверен, что понимаю, о чем вы спрашиваете, но, похоже, вам не хватает необходимого набора CRTP:

template<class T>
struct A {
  void func() {
    T& self = *static_cast<T*>(this);  // CRTP cast
    self.func();
  }
};

struct V : A<V> {  // B for the case of virtual func
  virtual void func() {
    std::cout << "V::func\n";
  }
};

struct NV : A<NV> {  // B for the case of non-virtual func
  void func() {
    std::cout << "NV::func\n";
  }
};

Если T не объявляет свой собственный func, это будет бесконечная рекурсия, поскольку self.func найдет A :: func. Это верно, даже если производный класс T (например, DV ниже) объявляет свой собственный функционал, но T этого не делает.

Тест с другим окончательным переопределением, чтобы показать отправку работ в соответствии с объявлением:

struct DV : V {
  virtual void func() {
    std::cout << "DV::func\n";
  }
};
struct DNV : NV {
  void func() {
    std::cout << "DNV::func\n";
  }
};

template<class B>
void call(A<B>& a) {
  a.func();  // always calls A<T>::func
}

int main() {
  DV dv;
  call(dv);   // uses virtual dispatch, finds DV::func
  DNV dnv;
  call(dnv);  // no virtual dispatch, finds NV::func

  return 0;
}
1 голос
/ 19 ноября 2010

Как вы могли бы реализовать класс A таким образом, чтобы, если B имеет виртуальное переопределение, то он отправлялся динамически, но статически отправлялся, если B нет?

Как заметили другие,понять этот вопрос очень сложно, но он заставил меня вспомнить кое-что, чему я научился давным-давно, так что вот очень длинный ответ на ваш вопрос:

template<typename Base> class A : private Base
{
public:
    void func()
    {
        std::count << "A::func";
    }
};

Учитывая это, это зависит отБаза A является ли func() виртуальной.Если Base объявит его virtual, то оно будет виртуальным и в A.В противном случае это не так.Смотрите это:

class V
{
public:
    virtual void func() {}
};
class NV
{
};

class B : public A<V>  // makes func() virtual
{
public:
    void func()
    {
        std::count << "B::func";
    }
};

class C : public A<NV>  // makes func() non-virtual
{
public:
    void func()
    {
        std::count << "C::func";
    }
};

Это может случиться, чтобы ответить на ваш вопрос?

1 голос
/ 19 ноября 2010

В вашем конкретном примере нет необходимости в динамической диспетчеризации, потому что тип c известен во время компиляции. Звонок на B::func будет жестко закодирован.

Если бы вы вызывали func через B*, то вы бы вызывали виртуальную функцию. Но в вашем очень надуманном примере это снова приведет вас к B::func.

Нет смысла говорить о динамической диспетчеризации из A*, поскольку A является классом шаблонов - вы не можете создать общий A, только тот, который привязан к определенному подклассу.

1 голос
/ 19 ноября 2010

Как вы могли бы реализовать класс A таким образом, чтобы, если B имеет виртуальное переопределение, что он динамически отправлялся, но статически отправлялся, если B нет?

Несколько противоречив, не так лине так ли?Пользователь класса A может ничего не знать о B или C. Если у вас есть ссылка на A, единственный способ узнать, нужна ли func() динамическая диспетчеризация, - это обратиться к vtable.Поскольку A::func() не является виртуальным, для него нет записи и, следовательно, некуда поместить информацию.Как только вы сделаете его виртуальным, вы обращаетесь к виртуальной таблице и ее динамической диспетчеризации.

Единственный способ получить прямые вызовы функций (или встроенные) - это использование не виртуальных функций и отсутствие косвенного обращения через указатели базового класса.

Редактировать: Я думаю, что идиома для этого в Scala будет class C: public B, public A<C> (повторяя черту с дочерним классом), но это не работает в C ++, потому что это делает члены A<T> неоднозначными в C.

0 голосов
/ 19 ноября 2010

Будет ли функция отправлена ​​динамически или нет, зависит от двух вещей:

a) является ли выражение объекта типом ссылки или указателя

b) является ли функция (для которой разрешением перегрузки)разрешается в) является виртуальным или нет.

Теперь перейдем к вашему коду:

  C c; 
  c.func();   // object expression is not of pointer/reference type. 
              // So static binding

  A <B> & ref = c; 
  ref.func(); // object expression is of reference type, but func is 
              // not virtual. So static binding


  A<D>* ptr = new D; 
  ptr->func(); // object expression is of pointer type, but func is not 
               // virtual. So static binding 

Короче говоря, 'func' не отправляется динамически.

Обратите внимание, что:: подавляет механизм вызова виртуальной функции.

$ 10.3 / 12- «Явная квалификация с помощью оператора области действия (5.1) подавляет механизм вызова виртуальной функции.

Код в OP2выдает ошибку, поскольку синтаксис X :: Y может использоваться для вызова «Y» в области «X», только если «Y» является статическим членом в области «X».

0 голосов
/ 19 ноября 2010

Кажется, вам просто нужно было добавить небольшую трассировку и использование, чтобы ответить на свой вопрос ...

#include <iostream>

template<typename T> struct A { 
    void func() { 
        T::func(); 
    } 
}; 

struct B1 : A<B1> { 
    virtual void func() { 
        std::cout << "virtual void B1::func();\n";
    } 
}; 

struct B2 : A<B2> { 
    void func() { 
        std::cout << "void B2::func();\n";
    } 
}; 

struct C1 : B1 { }; 
struct C2 : B2 { }; 

struct C1a : B1 {
    virtual void func() {
        std::cout << "virtual void C1a::func();\n";
    }
};

struct C2a : B2 {
    virtual void func() {
        std::cout << "virtual void C2a::func();\n";
    }
};

int main()
{
    C1 c1; 
    c1.func(); 

    C2 c2; 
    c2.func(); 

    B1* p_B1 = new C1a;
    p_B1->func();

    B2* p_B2 = new C2a;
    p_B2->func();
}

Вывод:

virtual void B1::func();
void B2::func();
virtual void C1a::func();
void B2::func();

Вывод: A действительно берет виртуальный-нессущество функции Б.

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