преобразование не шаблонной базы в дочерний класс не типового шаблона - PullRequest
0 голосов
/ 29 ноября 2018

У меня есть шаблонный тип не-типа template<std::size_t N> Derived<N>, который наследуется от некоторого базового класса не-шаблона Base:

class Base
{
public:
    double some_value;

    // Some methods and variables that do not depend on N
    // I wish to keep these in a non-templated class

    virtual const size_t get_N() = 0;
    virtual ~Base() = default;
    Base(double value): some_value {value} {};
};

template <std::size_t N>
class Derived: public Base
{
public:
    double some_other_value;

    // Some functions and variables, for which
    // the types and actions depend on N

    const size_t get_N() override
    {
        return N;
    }
    Derived(double value1, double value2): Base(value1), some_other_value {value2} {};
};

Теперь у меня есть функция call_by_base(Base& my_base), которая использует только переменные-члены/ функции объявлены в Base.Единственное исключение из этого, это звонок на template<std::size_t N> void call_by_derived(Derived& my_derived).Поскольку почти вся функция call_by_base не зависит от параметра шаблона, я бы предпочел оставить эту функцию без шаблонов.

Я попытался достичь вышеописанного с помощью реализаций, аналогичных следующим:

template<std::size_t N>
void call_by_derived(Derived<N>& my_derived)
{
    std::cout << "in call_by_derived" << std::endl;
    // Methods that use the functions and variables in Derived.
}

void broken_call_by_base(Base& my_base)
{
    std::cout << "in call_by_base" << std::endl;

    // Manipulations independent of child
    // type Derived<N>

    auto derived = dynamic_cast<Derived<my_base.get_N()>&>(my_base);
    call_by_derived(derived);

    // Manipulations independent of child
    // type Derived<N>
}

Когда я пытаюсь скомпилировать этот код, я получаю error: expression ‘Base::get_N’ is not a constant-expression.Я пытался устранить эту ошибку, пытаясь изменить что-то другое, как в моем базовом, так и в производном классе.Все это было безуспешно.

Мне удалось найти следующую альтернативу работе:

void working_call_by_base(Base& my_base)
{
    std::cout << "in call_by_base" << std::endl;

    if(my_base.get_N()==2)
    {
        auto derived = dynamic_cast<Derived<2>&>(my_base);
        call_by_derived(derived);
    }
    if(my_base.get_N()==3)
    {
        auto derived = dynamic_cast<Derived<3>&>(my_base);
        call_by_derived(derived);
    }
}

Это, однако, очень утомительно, особенно когда N может принимать гораздо больше значений.Есть ли способ заставить функцию работать по принципу broken_call_by_base?То есть: Как я могу преобразовать не шаблон Base в шаблон не-типа Derived<N>?

пс.Будет создан только объект типа Derived<N>.Это код в main для проверки:

int main()
{
    Derived<3> test(1.0,2.0);
        working_call_by_base(test);
        broken_call_by_base(test);
        call_by_derived(test);
}

1 Ответ

0 голосов
/ 29 ноября 2018

Будет лучше, если вы сможете использовать функцию-член virtual, чтобы избежать проверок if / else.Если по какой-то причине это не вариант, лучше использовать механизм обратного вызова / плагина.

  1. В базе кода, специфичного для Base, предусмотрен механизм, позволяющий другимклассы / функции / модули для регистрации функций, которые соответствуют типу, о котором они знают.

  2. В специфичном для Base коде вы отслеживаете зарегистрированные функции, используя клавишу, котораяподходит для Base.

  3. В специальном коде Base вы проверяете, была ли зарегистрирована функция для ключа.Если это так, вы вызываете функцию с подходящими аргументами.

  4. В коде, специфичном для производного класса, вы можете downcast перейти к соответствующему классу.Если downcast завершается успешно, в большинстве случаев вам следует перейти к использованию производного класса.

Этот шаблон строго придерживается принципа Open-Closed иэто один из моих любимых шаблонов кодирования.

В вашем случае ключ N.

Вот пример программы, которая демонстрирует концепцию.

#include <iostream>

// Base.hpp
// #pragma once

#include <cstdint>

class Base
{
   public:
      double some_value;

      // Some methods and variables that do not depend on N
      // I wish to keep these in a non-templated class

      virtual const size_t get_N() = 0;
      virtual ~Base() = default;
      Base(double value): some_value {value} {};

      typedef void (*CallbackFunctionType1)(Base& b);
      static void registerCallback(std::size_t N, CallbackFunctionType1 f);

};

void call_by_base(Base& my_base);

// Base.cpp
#include <map>

namespace BaseNS
{
   using CallbackFunctionType1Map = std::map<std::size_t, Base::CallbackFunctionType1>;

   CallbackFunctionType1Map& getCallbackFunctionType1Map()
   {
      static CallbackFunctionType1Map theMap;
      return theMap;
   }
}

void Base::registerCallback(std::size_t N, CallbackFunctionType1 f)
{
   BaseNS::CallbackFunctionType1Map& theMap = BaseNS::getCallbackFunctionType1Map();
   theMap[N] = f;
}

void call_by_base(Base& my_base)
{
   std::cout << "In call_by_base" << std::endl;
   BaseNS::CallbackFunctionType1Map& theMap = BaseNS::getCallbackFunctionType1Map();
   BaseNS::CallbackFunctionType1Map::iterator iter = theMap.find(my_base.get_N());
   if ( iter != theMap.end() )
   {
      iter->second(my_base);
   }
}

// Derived.hpp
// #pragma once

template <std::size_t N>
class Derived: public Base
{
   public:

      double some_other_value;

      // Some functions and variables, for which
      // the types and actions depend on N

      const size_t get_N() override
      {
         return N;
      }

      Derived(double value1, double value2): Base(value1), some_other_value {value2} {};
};

// Derived.cpp
// Register call back functions for Derived.

namespace DerivedNS
{
   template <std::size_t N>
      void call_by_derived(Derived<N>& derived)
      {
         std::cout << "In call_by_derived<" << N << ">" << std::endl;
         // Use derived.
      }


   template <std::size_t N>
      void call_for_derived(Base& my_base)
      {
         Derived<N>* d_ptr = dynamic_cast<Derived<N>*>(&my_base);
         if ( d_ptr != nullptr )
         {
            call_by_derived(*d_ptr);
         }
         else
         {
            // Error.
         }
      }

   bool registerCallbackFunctions()
   {
      // Register callbacks for as many values of N as needed.
      Base::registerCallback(1, call_for_derived<1>);
      Base::registerCallback(2, call_for_derived<2>);
      Base::registerCallback(3, call_for_derived<3>);
      Base::registerCallback(4, call_for_derived<4>);
      Base::registerCallback(5, call_for_derived<5>);
      return true;
   }

   bool dummy = registerCallbackFunctions();
}

int main()
{
   Derived<1> d1(0, 0);
   Derived<2> d2(0, 0);
   Derived<10> d3(0, 0);
   call_by_base(d1);
   call_by_base(d2);
   call_by_base(d3); // Does not go to call_by_derived.
}

Вывод:

In call_by_base
In call_by_derived<1>
In call_by_base
In call_by_derived<2>
In call_by_base
...