Возможен ли полиморфизм времени компиляции объектов, использующих массивы? - PullRequest
3 голосов
/ 04 апреля 2019

Я все еще учусь, и у меня есть вопрос, который я надеюсь, что кто-то может помочь мне с:

Можно ли достичь полиморфизма времени компиляции разных классов, наследующих один и тот же базовый класс для использования в массиве?без использования виртуальных функций?

Как любой, кто знает даже небольшой ООП с C ++, если у вас есть что-то вроде

class Base {
public:
   virtual void foo (){
    std::cout << "I'm the base" << std::endl;
   }
}

class A : public Base {
public:
  void foo() {
   std::cout << "I'm the child" << std::endl;
  }
} 

class B : public Base {
public:
  void foo() {
   std::cout << "I'm the child #2" << std::endl;
  }
} 


//Assume some main here

A *a = new A();
B *b = new B();

Base *someArray[] = {a,b};

someArray[0]->foo(); //"I'm the child"
someArray[1]->foo(); //"I'm the child #2"

Мы знаем, что это работает, потому что функции выполняются, а наследование разрешается во время выполнения.Однако что я должен знать, как это сделать без виртуальных функций или, если возможно, сделать то же самое без использования виртуальных функций?

Предположим, вы делаете что-то вроде этого

class Base {
public:
   void foo (){
    std::cout << "I'm the base" << std::endl;
   }
}

class A : public Base {
public:
  void foo() {
   std::cout << "I'm the child" << std::endl;
  }
} 

class B : public Base {
public:
  void foo() {
   std::cout << "I'm the child #2" << std::endl;
  }
} 


//Assume some main here

A *a = new A();
B *b = new B();

Base *someArray[] = {a,b};

someArray[0]->foo(); //"I'm the base"
someArray[1]->foo(); //"I'm the base"

ИтакЕсть много вариантов, но я не могу найти то, что будет иметь такое же поведение, как я хочу.Одно из решений, о котором я читал, - создание статического шаблона класса, который работает примерно так:

template <class T>
class Base
{
public:
    void interface()
    {
        // ...
        static_cast<T*>(this)->implementation();
        // ...
    }

    static void static_func()
    {
        // ...
        T::static_sub_func();
        // ...
    }
};

class Derived : Base<Derived>
{
public:
    void implementation() {
        std::cout << "I am derived" << std::endl;
    }
    static void static_sub_func();
};

class AlsoDerived : Base<Derived>
{
public:
    void implementation() {
        std::cout << "I am also derived" << std::endl;

    }
    static void static_sub_func();
};


//Assume in some main 

Derived div;
AlsoDerived alsoDiv;

Derived *someArray[] = { &div, &alsoDiv };//does not work not the same type 
Base *someArray[] = { &div, &alsoDiv }; //does not work b/c its a template 

Выше приведено не то поведение, которое мне нужно.И затем есть статическое приведение, которое кажется многообещающим, однако мне нужно знать, какой класс это будет в любой момент времени, а это не то поведение, которое я хочу.

class Base {
public:
   void foo (){
    std::cout << "I'm the base" << std::endl;
   }
}

class A : public Base {
public:
  void foo() {
   std::cout << "I'm the child" << std::endl;
  }
} 

class B : public Base {
public:
  void foo() {
   std::cout << "I'm the child #2" << std::endl;
  }
} 


//Assume some main here

A *a = new A();
A *b = new A();
B *c = new B();

Base *someArray[] = {a,b,c};

someArray[0]->foo(); //"I'm the base"
static_cast<A*>(someArray[1])->foo(); //"I'm the child"
static_cast<B*>(someArray[2])->foo(); //"I'm the child #2"

Это очень близко к тому, что я хочу, однако моя проблема в том, чтобы static_cast работал, мне нужно знать, какой это класс, я хочу использовать static, а не поведение, которое я хочу.

Что ты думаешь?Есть ли лучший способ сделать то, что я хочу?

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

1 Ответ

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

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

C ++ предлагает поведение, зависящее от типа во время выполнения, посредством полиморфизма, то есть с виртуальными функциями, и RTTI , если необходимо. Если вы не хотите использовать этот «естественный способ», у вас есть следующие альтернативы:

  • Вы знаете тип объектов во время компиляции (например, если вы уверены, что первый элемент всегда равен A*, а второй всегда B*): в этом случае вы можете использовать static_cast как в третьем случае, или используйте шаблонный код (например, если вы предпочитаете политики времени компиляции вместо стратегии времени выполнения ). Но обычно это не способ обработки случайных объектов в контейнере.
  • Вы можете самостоятельно определить тип объекта и определить соответствующую функцию для вызова: типичный способ сделать это - иметь поле ввода в базовом классе и использовать его для понижения с помощью static_cast. Но это не лучшая практика. И добавление вашей собственной RTTI-подобной функциональности вместо использования оптимизированной компилятором не будет более эффективным, чем полиморфизм.
  • Вы можете использовать std::variant или std::any: они предоставляют возможность предлагать какое-то поведение, зависящее от времени выполнения, с использованием функций времени компиляции (без необходимости требуя, чтобы альтернативные типы были получены из общей базы).

Остается вопрос: почему вы хотите избежать нормального полиморфизма, если он вам нужен?

...