Making double and std::vector<double> covariant

独自空忆成欢 提交于 2021-01-29 21:51:30

问题


I am trying to make a wrapper for "any" data type such that they have common interface called IValue so it will be possible to call get() on any concrete Value and return the value of concrete data type. In simplest case I just want to be able to call get() on double and std::vector<double>. In my understanding these data types need to be covariant (which it is not at all in my code). The following is my raw imagination of the code:

//template<typename T>
class IValue 
{
protected:
    // typedef std::variant<T, std::vector<T>> return_type; <- this was an approach
public:
    IValue() {}

    virtual int size() = 0;

    virtual /*some special type*/ get() = 0;
};


template<typename T>
class Scalar : public IValue<T> {
    T data = NULL;
public:

    Scalar(T new_data) : data(new_data) {}

    T get() { return data; }

    int size()  { return 1; }
};


template<typename T>
class Vector : public IValue<T> 
{
    std::vector<T> data;
public:

    Vector(std::vector<T> new_data) : data(new_data) {}

    std::vector<T> get() { return data; }

    T get_element(int index) { return data[index]; }

    int size()  { return data.size(); }
};

I am using c++17 on VS17.


回答1:


You're indeed almost there. std::variant<T, std::vector<T>> is indeed the correct return type, and your get() { return data; } implementations are also correct. Literally the only big problem is that the return type of all get overrides should be std::variant<T, std::vector<T>>.

Stylewise, get should be const and adding override helps to improve error messages.




回答2:


Covariance only applies to pointer/reference of polymorphic types.

so you have to wrap double/vector<double> in some class:

struct IWrapper
{
    virtual ~IWrapper() = default;
};

struct DoubleWrapper : IWrapper
{
    double d = 0.;
};

struct DoubleVecWrapper : IWrapper
{
    std::vector<double> v;
};

And then you can have something like:

class IValue 
{
public:
    virtual ~IValue() = default

    virtual int size() = 0;
    virtual IWrapper& get() = 0;
};


class Scalar : public IValue {
    DoubleWrapper data;
public:
    explicit Scalar(double d) : data(d) {}

    T& get() override { return data; }
    int size() override { return 1; }
};


class Vector : public IValue
{
    DoubleVecWrapper data;
public:

    Vector(const std::vector<T>& v) : data(v) {}

    DoubleVecWrapper& get() override { return data; }
    int size() override { return data.v.size(); }

    T get_element(int index) { return data[index]; }
};

but without meaningful interface, it is mostly useless

// No use of Base interface or inheritance, so ok
void foo(Scalar& value)
{
    const auto size = value.size();
    auto& wrapper = value.get(); // auto& is DoubleWrapper&
    wrapper.d = 4.2;
}

// But, with base class, we cannot go really far:
void foo(IValue& value)
{
    const auto size = value.size();
    auto& wrapper = value.get(); // auto& is IWrapper&
    // What to do now? IWrapper can't do anything
}


来源:https://stackoverflow.com/questions/60434788/making-double-and-stdvectordouble-covariant

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!