C++ 多态值类型和接口
我有一个多态值类型,实现方式如下:C++ 多态值类型和接口,c++,polymorphism,c++17,C++,Polymorphism,C++17,我有一个多态值类型,实现方式如下: class ShapeValue { public: template<class T> ShapeValue(const T& value) { obj = make_unique<holder<T>>(value); } // ... appropriate copy constructors and such void draw() { obj->
class ShapeValue {
public:
template<class T>
ShapeValue(const T& value) {
obj = make_unique<holder<T>>(value);
}
// ... appropriate copy constructors and such
void draw() { obj->draw(); }
private:
struct base {
virtual ~base() {}
virtual void draw() = 0;
};
template<class T>
struct holder<T> : public base {
T value;
void draw() override { value.draw(); }
}
unique_ptr<base> obj;
};
然后我会定义其他接口,比如:
class HasColor {
virtual Color color() = 0;
virtual void setColor(Color) = 0;
};
所以我可以定义一个形状如下:
class MyShape : public Shape, public HasColor {
void draw() override;
Color color() override;
void setColor(Color) override;
};
因此,如果我有一组选定的形状,并且我想设置它们的颜色,我可以迭代所有的形状和dynamic\u cast
。这证明非常方便(顺便说一句,我的实际应用程序不是绘图应用程序,但有类似的数据)
我可以为我的多态值类型这样做吗,我的ShapeValue
接口不需要知道每个都有接口?
我可以做以下事情,这其实并不坏,但并不理想:
HasColor* ShapeValue::toHasColor() { return obj->toHasColor(); }
解决方案(已测试)是为接口提供基类:
class AnyInterface {
virtual ~AnyInterface() {} // make it polymorphic
};
struct HasColor : public AnyInterface {
// ... same stuff
};
那么我们有以下几点:
vector<AnyInterface*> ShapeValue::getInterfaces() { return _obj->getInterfaces(); }
vector ShapeValue::getInterfaces(){return\u obj->getInterfaces();}
然后可以定义一个助手来获取我们想要的接口:
template<class I>
I* hasInterface(Shape& shape) {
for(auto interface : shape.getInterfaces()) {
if(auto p = dynamic_cast<I*>(interface)) {
return p;
}
}
return nullptr;
}
模板
I*hasInterface(形状和形状){
for(自动接口:shape.getInterfaces()){
if(自动p=动态施法(界面)){
返回p;
}
}
返回空ptr;
}
通过这种方式,
ShapeValue
不需要了解所有接口类型。接受的答案似乎是一个可行的解决方案,尽管我还没有测试过它,而且它似乎回到了引用语义。然而,多态值类型的一个激励因素是值语义
下面是一个更具价值的面向语义的替代解决方案的描述,ShapeValue
不需要了解所有的接口类型,尽管外部用户可定义的自由函数是这样做的
由于我一直在使用多态值类型,我更喜欢识别这些值的两类功能:
基本多态概念类的虚拟方法强制实现的功能
ShapeValue
接口不需要了解每个可选扩展。作为额外的好处,不需要使用类型多态性来实现扩展功能,即符合ShapeValue
构造条件的值类型不必具有任何类型的继承关系或虚拟函数
下面是一个伪代码示例,用于扩展问题代码:
class ShapeValue {
public:
template<class T>
ShapeValue(const T& value) {
obj = make_unique<holder<T>>(value);
}
// ... appropriate copy constructors and such
ShapeValue& operator=(const ShapeValue& newValue) {
obj = newValue.obj? newValue.obj->clone(): nullptr;
return *this;
}
const std::type_info& type() const noexcept {
return obj? obj->type_(): typeid(void);
}
void draw() { obj->draw(); }
template <typename T>
friend auto type_cast(const ShapeValue* value) noexcept {
if (!value || value->type() != typeid(std::remove_pointer_t<T>))
return static_cast<T>(nullptr);
return static_cast<T>(value->obj->data_());
}
private:
struct base {
virtual ~base() = default;
virtual void draw() = 0;
virtual std::unique_ptr<base> clone_() const = 0;
virtual const std::type_info& type_() const noexcept = 0;
virtual const void* data_() const noexcept = 0;
};
template<class T>
struct holder final: base {
T value;
void draw() override { value.draw(); }
std::unique_ptr<base> clone_() const override {
return std::make_unique<holder>(value);
}
const std::type_info& type_() const noexcept override { return typeid(T); }
const void* data_() const noexcept override { return &value; }
};
unique_ptr<base> obj;
};
template <typename T>
inline auto type_cast(const ShapeValue& value)
{
auto tmp = type_cast<std::add_pointer_t<std::add_const_t<T>>>(&value);
if (tmp == nullptr)
throw std::bad_cast();
return *tmp;
}
struct Square {
int side_;
Color color_;
void draw();
Color color() { return color_; }
void setColor(Color value) { color_ = value; }
};
Color color(const ShapeValue& value)
{
if (value.type() == typeid(Square)) {
return type_cast<Square>(value).color();
}
throw std::invalid_argument("color not supported for value's type");
}
void setColor(ShapeValue& value, Color newColor)
{
if (value.type() == typeid(Square)) {
auto square = type_cast<Square>(value);
square.setColor(newColor);
value = square;
return;
}
throw std::invalid_argument("setColor not supported for value's type");
}
类ShapeValue{
公众:
模板
形状值(常数T和值){
obj=使_唯一(值);
}
//…适当的复制构造函数等
ShapeValue和运算符=(常量ShapeValue和newValue){
obj=newValue.obj?newValue.obj->clone():nullptr;
归还*这个;
}
const std::type_info&type()const noexcept{
返回obj?obj->type_2;():typeid(void);
}
void draw(){obj->draw();}
模板
friend自动类型_cast(const ShapeValue*值)无例外{
如果(!value | | value->type()!=typeid(std::remove_pointer_t))
返回静态_cast(nullptr);
返回静态_cast(值->对象->数据_());
}
私人:
结构基{
virtual~base()=默认值;
虚空绘制()=0;
虚拟std::unique_ptr clone_u()常量=0;
虚拟常量std::type_info&type_u()常量noexcept=0;
虚拟常量void*data_uuxcept()常量noexcept=0;
};
模板
结构持有者最终版本:基础{
T值;
void draw()重写{value.draw();}
std::unique_ptr clone_uz()常量覆盖{
返回标准::使_唯一(值);
}
const std::type_info&type_()const noexcept override{return typeid(T);}
const void*data_u389;()const noexcept override{return&value;}
};
独特的_ptrobj;
};
模板
内联自动类型转换(常量形状值和值)
{
自动tmp=类型转换(&值);
如果(tmp==nullptr)
抛出std::bad_cast();
返回*tmp;
}
结构广场{
内侧面;
颜色;
无效抽取();
Color Color(){return Color;}
void setColor(颜色值){Color\u0=value;}
};
颜色(常量形状值和值)
{
if(value.type()==typeid(正方形)){
返回类型_cast(value).color();
}
throw std::无效的_参数(“值的类型不支持颜色”);
}
void setColor(ShapeValue&value,Color newColor)
{
if(value.type()==typeid(正方形)){
自动平方=类型(值);
正方形。设置颜色(新颜色);
值=平方;
返回;
}
抛出std::无效的_参数(“值的类型不支持setColor”);
}
对于更详细、可编译、已测试和
typeid
/std::type_info
-免费的示例,可以查看我刚刚完成的多态值类型的源代码,它提供了一个值类型接口,用于约束一个或多个实体的移动。我不认为它是完美的,但它也更倾向于值语义,就像我在这个答案中包含的上面的例子一样。这不起作用,因为接口实际上没有在他的值多态系统中使用。@Yakk AdamNevraumont我写了一个单元测试,它起作用了。你担心的是二进制代码膨胀还是手写代码膨胀,生成的代码膨胀,有一个中心列表,还是什么?“膨胀”对我来说仍然很模糊。你知道传统的C++虚拟表在幕后生成什么吗?
class ShapeValue {
public:
template<class T>
ShapeValue(const T& value) {
obj = make_unique<holder<T>>(value);
}
// ... appropriate copy constructors and such
ShapeValue& operator=(const ShapeValue& newValue) {
obj = newValue.obj? newValue.obj->clone(): nullptr;
return *this;
}
const std::type_info& type() const noexcept {
return obj? obj->type_(): typeid(void);
}
void draw() { obj->draw(); }
template <typename T>
friend auto type_cast(const ShapeValue* value) noexcept {
if (!value || value->type() != typeid(std::remove_pointer_t<T>))
return static_cast<T>(nullptr);
return static_cast<T>(value->obj->data_());
}
private:
struct base {
virtual ~base() = default;
virtual void draw() = 0;
virtual std::unique_ptr<base> clone_() const = 0;
virtual const std::type_info& type_() const noexcept = 0;
virtual const void* data_() const noexcept = 0;
};
template<class T>
struct holder final: base {
T value;
void draw() override { value.draw(); }
std::unique_ptr<base> clone_() const override {
return std::make_unique<holder>(value);
}
const std::type_info& type_() const noexcept override { return typeid(T); }
const void* data_() const noexcept override { return &value; }
};
unique_ptr<base> obj;
};
template <typename T>
inline auto type_cast(const ShapeValue& value)
{
auto tmp = type_cast<std::add_pointer_t<std::add_const_t<T>>>(&value);
if (tmp == nullptr)
throw std::bad_cast();
return *tmp;
}
struct Square {
int side_;
Color color_;
void draw();
Color color() { return color_; }
void setColor(Color value) { color_ = value; }
};
Color color(const ShapeValue& value)
{
if (value.type() == typeid(Square)) {
return type_cast<Square>(value).color();
}
throw std::invalid_argument("color not supported for value's type");
}
void setColor(ShapeValue& value, Color newColor)
{
if (value.type() == typeid(Square)) {
auto square = type_cast<Square>(value);
square.setColor(newColor);
value = square;
return;
}
throw std::invalid_argument("setColor not supported for value's type");
}