我有这个例子:
class Entity
{
float mX;
float mY;
string mName;
// other attributes
public:
void setPosX(float x);
void setPosY(float y);
void setName(string name);
// other setters
template<typename T>
virtual void setAttributeValue(string param, T value)
{
if(param == "x")
setX(value);
else if(param == "y")
setY(value);
else if(param == "name")
setName(value);
// ...
}
};
class SpecialEntity : public Entity
{
int specialAttr;
// other special attributes
public:
void setSpecialAttr(int val);
// other setters
template<typename T>
virtual void setAttributeValue(string param, T value)
{
Entity::setAttributeValue(param, value);
if(param == "specialAttr")
setSpecialAttr(value);
// ...
}
};
由于不允许使用模板化虚拟方法,因此无法编译。
我在我的编辑器应用程序中需要这个,它有一个属性网格控件,根据该控件中属性的名称,我需要从Entity类或继承的Entity类调用一个方法来设置一个属性值。
实现这一目标的最佳方法是什么。
答案 0 :(得分:2)
当我必须这样做时,我使用的其中一个选项是传递boost::any
而不是T
。
virtual void setAttributeValue(string param, boost::any value)
{
if(param == "x")
setX(boost::any_cast<int>(value));
else if(param == "y")
setY(boost::any_cast<int>(value));
else if(param == "name")
setName(boost::any_cast<std::string>(value));
// ...
}
答案 1 :(得分:1)
以老式的方式做到这一点
setX(int)
setY(float)
setZ(string)
更安全(编译器会发现错误)和更快
答案 2 :(得分:0)
您无法制作类似虚拟或其他模板的功能。即使您从未打算调用它们,模板函数的每个版本中的所有函数调用都必须编译。
模板函数发生的所有事情都是函数的副本,并根据需要用T
替换为每种类型。如果你手工完成,很容易看出它不会编译。在这种情况下,没有setX
获取string
而setName
没有获得float
:
class Entity
{
float mX;
std::string mName;
public:
void setX(float x){ mX = x; }
void setName(std::string name){ mName = name; }
void setAttributeValue(std::string param, float value)
{
if(param == "x")
setX(value);
else if(param == "name")
setName(value); // Error!
}
void setAttributeValue(std::string param, std::string value)
{
if(param == "x")
setX(value); // Error!
else if(param == "name")
setName(value);
}
};
我建议你使setAttributeValue
虚拟和非模板化,并传入一种可以转换为任何类型的类型。也许是string
,boost::any
或boost::variant
。
如果您无法使用boost::any
或boost::variant
,则可以创建自己的Value
接口以传入:
struct Value {
virtual float getFloat() const = 0;
virtual std::string getString() const = 0;
virtual int getInt() const = 0;
};
struct ValueBase : Value {
float getFloat() const override { throw std::runtime_error("Not float"); }
std::string getString() const override { throw std::runtime_error("Not string"); }
int getInt() const override { throw std::runtime_error("Not int"); }
};
struct FloatValue : ValueBase {
float value;
FloatValue(float value) : value(value){}
float getFloat() const override { return value; }
};
struct StringValue : ValueBase {
std::string value;
StringValue(std::string value) : value(value){}
std::string getString() const override { return value; }
};
struct IntValue : ValueBase {
int value;
IntValue(int value) : value(value){}
int getInt() const override { return value; }
};
class Entity {
float mX;
float mY;
std::string mName;
public:
void setX(float x);
void setY(float y);
void setName(std::string name);
virtual void setAttributeValue(std::string param, const Value& value) {
if(param == "x")
setX(value.getFloat());
else if(param == "y")
setY(value.getFloat());
else if(param == "name")
setName(value.getString());
}
};
class SpecialEntity : public Entity {
int specialAttr;
public:
void setSpecialAttr(int val);
void setAttributeValue(std::string param, const Value& value) override {
Entity::setAttributeValue(param, value);
if(param == "specialAttr")
setSpecialAttr(value.getInt());
}
};
答案 3 :(得分:0)
您可以使用奇怪的重复模板模式,多态基类,一些标记和多态值类来实现您所需的功能
首先我们需要一个多态值类型:
$("table td").filter(function(){ return $(this).text() == "lucy" });
然后我们的静态(CRTP)和动态多态性:
struct value{ virtual ~value(){} };
struct text_value: public value{ text_value(const std::string &str_): str(str_){} std::string str; };
struct int_value: public value{ int_value(int i_): i(i_){} int i; };
auto make_value(const std::string &str){ return text_value{str}; }
auto make_value(int i){ return int_value{i}; }
然后我们可以开始定义一些实现我们接口的类! (enum class entity_tag{
text
};
class entity{
public:
entity(entity_tag tag_): tag(tag_){}
virtual ~entity(){}
entity_tag tag;
};
template<typename Entity>
class entity_base: entity{
public:
entity_base(): entity(this->get_tag()){}
template<typename T>
void set_value(const std::string ¶m, T &&val){
reinterpret_cast<Entity*>(this)->set_value_impl(param, std::forward<T>(val));
}
static entity_tag get_tag(){
return Entity::get_tag_impl();
}
};
和set_value_impl
)
get_tag_impl
它就像1,2,3一样简单!
您可以按照自己想要的方式使用它:
class text_entity: public entity_base<text_entity>{
protected:
std::string font = "times new roman";
int font_size = 10;
std::string text = "";
void set_text(value &&v){
auto tv = dynamic_cast<text_value&&>(v);
text = std::move(tv.str);
}
void set_font(value &&v){
auto tv = dynamic_cast<text_value&&>(v);
font = std::move(tv.str);
}
void set_font_size(value &&v){
auto iv = dynamic_cast<int_value&&>(v);
font_size = iv.i;
}
public:
static entity_tag get_tag_impl(){ return entity_tag::text; }
template<typename T>
void set_value_impl(const std::string &str, T &&val){
auto v = make_value(val);
if(str == "text")
set_text(std::move(v));
else if(str == "font")
set_font(std::move(v));
else if(str == "font_size")
set_font_size(std::move(v));
else
throw "invalid parameter";
}
};
它也不会让你尝试分配,例如int main(){
text_entity text;
text.set_value("font", "comic sans");
text.set_value("font_size", 24);
text.set_value("text", "Comic sans sucks");
}
到float
,因为"font"
没有值类型。如果有,它会在float
内抛出异常。
您可以在每个set_font
子类中定义value
子类,这样如果给出的值对于任何给定参数都不可能,编译器将始终抱怨。
您还可以将实体存储在具有其他实体类型的容器中:
entity
但要访问每个int main(){
std::vector<entity*> entities;
text_entity text0;
text_entity text1;
pic_entity pic0;
slider_entity slider0;
entities.push_back(&text0);
entities.push_back(&text1);
entities.push_back(&pic0);
entities.push_back(&slider0);
}
的{{1}}函数,您需要查看与每个变量关联的set_value
变量:
entity
您可以为tag
类型使用template<typename T>
void set_value_of_text(entity *e, const std::string ¶m, T &&t){
if(e->tag != entity_tag::text)
throw "entity is not a text entity";
dynamic_cast<text_entity*>(e)->set_value(param, std::forward<T>(t));
}
以外的其他内容,以便日后添加实体类型