我有与此question
相似的用例我想检查参数中存储了哪种类型的实例变量而不会引发异常
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,因此无法使用变体或任何
是否有执行此操作的标准方法。我想要的只是一个类的实例变量,该实例变量可以是多种类型(有界),在读取时检查其类型
答案 0 :(得分:3)
解决此问题的简单方法是向指针上的is<T>()
定义的模板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
作为指针存在才能正确操作;价值语义学将更加连贯(如果可能)
即使可以使用类型擦除,也可以使用类型擦除,这会更好(这是C ++ 17的Parameter
会为您完成的工作)。您所链接问题中的second answer已经描述了外观。
这使用了诸如转发引用,右值引用和std::any
之类的C ++ 11功能-但该概念也可以应用于早期的C ++版本。
对于类型擦除,您需要一个至少包含以下两个功能的接口:
由于C ++中的接口不能为unique_ptr
,因此我们必须在返回引用方面颇具创意。 C ++具有virtual
,可以是任何类型的指针。如果滥用(例如在错误的类型之间进行转换),这可能会很糟糕;但是如果我们知道底层类型,那就可以了。幸运的是,在这里,我们知道了底层类型。
可以通过以下方式来实现快速的类型擦除:
void*
在上面,我们自己承担检测基础类型的负担-但我们删除了循环耦合。现在,我们还有一个合适的值类型,可以像普通变量一样四处移动,这非常有帮助,因为它允许我们从API返回此对象,而不必担心生存期或所有权。
如果还需要可复制性,则可以扩展该接口以使其具有#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);
}
};
功能或返回副本的功能
使用该对象,代码变为:
clone()
这里的工作量很小example。
如果您正在寻找一组有限的实例化,std::variant
可以用于此目的。如果可能的基础类型的数量是 unbounded ,则应查看std::any
在任何一种情况下,这里的层次结构都是肤浅的(至少在当前示例中如此),因为可以将整个类型擦除减少为具有查询包含项能力的单数类型。以if (parameter.is<int>()) {
/* treat parameter as an int */
}
为例,可以很容易地做到这一点:
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;
};
并以某种方式处理错误来限制它。
只需执行以下操作即可查询活动类型:
is<T>()
如果类型是固定的,则始终可以使用if (parameter.is<int>()) {
/* treat parameter as an int */
}
代替std::variant
来定义std::has_alternative
答案 1 :(得分:0)
看起来像您预先知道了该参数的所有可能类型(我之所以这样说是因为您有一个type
字段,期望将其用作枚举)。在这种情况下,您可以使用std::variant
惯用语:
class Diagram
{
public:
std::variant<Parameter<int>, Parameter<std::string>> v;
};
在这种情况下,您可以使用以下代码来了解实际类型:
switch(v.index()) {
case 0:
// int is used
case 1:
// string is used
}
请确保还有其他选择。例如,如果您有某种类型的东西,并且需要测试这是否是期望的类型,那么您可以使用std::is_same
模板:
template <typename T>
class Parameter : public ParameterBase
{
public:
bool isOfTypeInt() const {
return std::is_same_v<T, int>;
}
private:
T value;
};