Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/150.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 多态值类型和接口_C++_Polymorphism_C++17 - Fatal编程技术网

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");
    }