问题
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