我有类似于这个问题用例
我想检查什么类型的实例变量存储在参数中而不引发异常
class ParameterBase
{
public:
virtual ~ParameterBase() {}
template<class T> const T& get() const; //to be implimented after Parameter
template<class T, class U> void setValue(const U& rhs); //to be implimented after Parameter
};
template <typename T>
class Parameter : public ParameterBase
{
public:
Parameter(const T& rhs) :value(rhs) {}
const T& get() const {return value;}
void setValue(const T& rhs) {value=rhs;}
private:
T value;
};
//Here's the trick: dynamic_cast rather than virtual
template<class T> const T& ParameterBase::get() const
{ return dynamic_cast<const Parameter<T>&>(*this).get(); }
template<class T, class U> void ParameterBase::setValue(const U& rhs)
{ return dynamic_cast<Parameter<T>&>(*this).setValue(rhs); }
class Diagram
{
public:
ParameterBase* v;
int type;
};
我想做的是这样的事情
if (diagram.getParameter().type == int) {
}
我如何改变这个实现,使它允许我窥视什么类型的参数是持有的
谢谢你的回答,还有几点
我是C++11所以不能使用variant或any
有没有标准的方法。我想要的是一个实例变量的类,可以是多种类型(有界的),并在阅读它时,检查它是什么类型
解决问题的简单方法是将模板函数is
添加到您的ParameterBase
中,ParameterBase
是根据指针上的dynamic_cast
定义的。带有指针的dynamic_cast
在失败时返回nullptr
,这与引用不同,引用将抛出std::bad_cast
。例如:
class ParameterBase
{
public:
...
template <typename T>
bool is() const;
};
...
template <typename T>
bool ParameterBase::is() const
{
return dynamic_cast<const Parameter<T>*>(this) != nullptr;
}
其用途很简单:
if (diagram.getParameter().is<int>()) {
...
}
不过,请注意,这整个设计并不是特别好。它在基函数和派生函数之间具有循环依赖关系,这种依赖关系是高度耦合的。此外,它要求parameterbase
作为指针存在,以便正确操作;其中值语义将更加连贯(如果可能的话)
如果您能够使用类型擦除就更好了,即使您定义了parameter
(C++17的std::any
将为您做这件事)。您的链接问题中的第二个答案已经描述了这可能是什么样子的。
这使用了C++11的功能,如转发引用、rvalue-references和unique_ptr
,但这个概念也可以应用于早期的C++版本。
对于类型擦除,您需要一个至少包含以下2个特性的接口:
由于C++中的接口不能是virtual
,所以我们必须在返回引用方面进行创新。C++有void*
,它可以是任何类型的指针。如果使用不当(例如在错误的类型之间进行转换),这可能是不好的;但如果我们知道底层类型,就可以完美。谢天谢地,我们知道了底层类型。
#include <type_traits> // std::decay
#include <utility> // std::forward
#include <typeinfo> // std::type_info, std::bad_cast
#include <memory> // std::unique_ptr
class Parameter
{
private:
// This is the interface we will implement in all instances
struct Interface {
virtual ~Interface() = default;
virtual void* get() = 0;
virtual const std::type_info& type() const = 0;
};
// This is the concrete instantiation of the above interfaces
template <typename T>
struct Concrete : public Interface {
template <typename U>
Concrete(U&& u) : m_value{std::forward<U>(u)} {}
void* get() { return &m_value; }
const std::type_info& type() const { return typeid(T); }
T m_value; // actually holds the value here
};
// This holds onto the interface, and only the interface
std::unique_ptr<Interface> m_interface;
public:
// Constructs a parameter and sets the first interface value
template <typename T>
explicit Parameter(T&& value)
: m_interface{new Concrete<typename std::decay<T>::type>{std::forward<T>(value)}}
{}
Parameter(Parameter&&) = default;
Parameter& operator=(Parameter&&) = default;
// Check if we are the same type by comparing the typeid
template <typename T>
bool is() const {
return typeid(T) == m_interface->type();
}
// Get the underlying value. Always check that we are the correct type first!
template <typename T>
const T& get() const {
// do the error handling ourselves
if (!is<T>()) { throw std::bad_cast{}; }
// cast void* to the underlying T*. We know this is safe
// because of our check above first
return (*static_cast<T*>(m_interface->get()));
}
// Set the underlying value. Always check that we are the correct type first!
template <typename T, typename U>
void set(U&& value) {
// do the error handling ourselves
if (!is<T>()) { throw std::bad_cast{}; }
(*static_cast<T*>(m_interface->get())) = std::forward<U>(value);
}
};
在上面,我们自己承担检测底层类型的任务--但是我们去掉了循环耦合。我们现在也有了一个适当的值类型,我们可以像普通变量一样四处移动,这真的很有帮助,因为它允许我们从API返回这个对象,而不用担心生存期或所有权问题。
如果还需要可复制性,则可以扩展接口,使其具有clone()
函数或返回副本的其他功能
使用此对象,代码变为:
if (parameter.is<int>()) {
/* treat parameter as an int */
}
这里有一个工作的小例子。
如果您正在寻找一组有限的实例化,可以使用std::variant
。如果可能的基础类型的数量是无界的,则应查看std::any
不管是哪种情况,在这里使用层次结构都是肤浅的(至少在当前示例中是这样),因为整个类型擦除可以被简化为具有查询包含能力的单数类型。使用std::any
作为示例,可以很容易地完成此操作:
#include <any> // std::any, std::any_cast
class Parameter
{
public:
// This implementation changes the active type if 'T' is not the same as the stored
// value. If you want to restrict this, you can do error checking here instead.
template <typename T>
void set(const T& value) { m_value = value; }
template <typename T>
const T& get() { return std::any_cast<const T&>(m_value); }
template <typename T>
bool is() const noexcept { return m_value.type() == typeid(T); }
private:
std::any m_value;
};
如果您不希望活动成员更改,可以通过首先检查是
并以某种方式处理错误来限制这一点。
查询活动类型只需执行以下操作即可:
if (parameter.is<int>()) {
/* treat parameter as an int */
}
如果类型是固定的,则始终可以使用std::variant
而不是使用std::has_alternative
来定义is
我的函数使用一组给定的输入参数(变量)调用Python函数,并返回包含函数输出的元组(变量,因为输出随调用的函数而变化)。 我正在使用C 11通过MinGW-w64编译器的g端口在视窗10机器上编译。我声明了这个模板变量函数(并调用它)如下: 但是,会引发此错误(为了可读性,缩短为):
我想实例化一个可变模板类
变量通用属性 变量通用属性有title,value,type,tip,rule,message,除了通用属性个别变量还有其它属性,请看每个具体控件; "vars": { "varName1": { "title": "测试 text", /*后台设置时 input 的 label*/ "value": "1", /*变量默认值*/ "type
变量通用属性 变量通用属性有title,value,type,tip,rule,message,除了通用属性个别变量还有其它属性,请看每个具体控件; "vars": { "varName1": { "title": "测试 text", /*后台设置时 input 的 label*/ "value": "1", /*变量默认值*/ "type
我很难弄清楚如何使用适当的模板化参数调用setValue函数。在ParameterBase抽象基类中不可能有模板化的参数。非常感谢任何帮助。 附注。我没有使用boost::any的灵活性。