将非模板基础转换为非类型模板子类

时间:2018-11-29 16:21:00

标签: c++ templates inheritance c++14 downcast

我有一个非类型模板类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参数,因此我希望将此功能保持非模板状态。

我试图通过以下方式实现上述目标:

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。我试图通过尝试在Base和Derived类中更改其他内容来解决此错误。这一切都没有成功。

我设法找到了以下替代方法:

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>

ps。仅创建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 :(得分:1)

最好是可以使用virtual成员函数来避免if / else的检查。 如果由于某种原因这不是一个选择,那么最适合使用回调/插件机制。

  1. 您可以在Base特定的代码库中提供一种机制,以允许其他类/函数/模块注册适合于他们所知道的类型的函数。

  2. 在特定于Base的代码中,您使用适合于Base的密钥来跟踪已注册的功能。

  3. 在特定于Base的代码中,检查是否已为该键注册了功能。如果有的话,请使用适当的参数调用该函数。

  4. 在派生类特定的代码中,您可以downcast进入相应的类。如果downcast成功,在大多数情况下应该成功,您将继续使用派生类。

此模式严格遵守Open-Closed Principle,是我最喜欢的编码模式之一。

在您的情况下,密钥为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