我有一个非类型模板类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);
}
答案 0 :(得分:1)
最好是可以使用virtual
成员函数来避免if
/ else
的检查。
如果由于某种原因这不是一个选择,那么最适合使用回调/插件机制。
您可以在Base
特定的代码库中提供一种机制,以允许其他类/函数/模块注册适合于他们所知道的类型的函数。
在特定于Base
的代码中,您使用适合于Base
的密钥来跟踪已注册的功能。
在特定于Base
的代码中,检查是否已为该键注册了功能。如果有的话,请使用适当的参数调用该函数。
在派生类特定的代码中,您可以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