Use case:
Vector
class (implementing some math) and a derived Vector2D
class Vector
namespace mu {
template<std::size_t N, typename T>
class Vector {
public:
// ...
template <typename... TArgs>
Vector(TArgs... args) : data({args...}) {}
Vector(const Vector &other) = default; // copy constructor
// ...
protected:
std::array<T, N> data;
};
}
Vector2D
namespace mu {
template<typename T>
class Vector2D : public Vector<2,T> {
public:
using Vector<2, T>::Vector; // inherit base class constructors
Vector2D(const Vector<2, T>& other) : Vector<2, T>(other) {}
// Vector2D specific functions, e.g. rotation
//...
};
}
note: the actual classes contain a lot more but i condensed it down to the code that i think is most important here.
The problem is that i'm not able to implement a way such that a Vector
can be constructed from a Vector2D
, see code below. All other cases work fine.
// Example 1 (compiles)
mu::Vector<2, int> a{1, 2};
mu::Vector<2, int> b{a};
// Example 2 (compiles)
mu::Vector2D<int> c{1, 2};
mu::Vector2D<int> d{c};
// Example 3 (compiles)
mu::Vector<2, int> e{1, 2};
mu::Vector2D<int> f{e};
// Example 4 (doesn't compile) <-- how to get this to work?
mu::Vector2D<int> g{1, 2};
mu::Vector<2, int> h{g};
Of course the more general question would be if inheritance is the right way to structure these classes. But i'd like Vector2D
to have all the functionality of Vector
and also additional functions that the Vector
does not have.
Your Vector
class has two constructor: a template one (intended for values) and the default copy constructor.
Problem: the copy constructor is preferred but only if there is an exact match.
So, initializing b
with a
mu::Vector<2, int> a{1, 2};
mu::Vector<2, int> b{a};
the copy constructor is preferred because a
is an exact match
But, initializing h
with g
mu::Vector2D<int> g{1, 2};
mu::Vector<2, int> h{g};
g
can be converted to a mu::Vector<2, int>
but isn't an exact match, so the template constructor is preferred but the template constructor is incompatible.
A possible solution: SFINAE disable the template constructor when the there is only one argument and the argument is derived from mu::Vector
.
For example
template <typename... TArgs,
typename std::enable_if_t<sizeof...(TArgs) == N
or (not std::is_base_of_v<Vector, TArgs> && ...), int> = 0>
Vector(TArgs const & ... args) : data({args...}) {}
Leaving aside suitability of inheritance for this specific task, the immediate reason for the failure is the catch-all template <typename... TArgs>
constructor. It will intercept every construction which is not a copy—and construction from a derived class object is not a copy. This is because conversion from Derived to Base is a conversion, and the template constructor doesn't require a conversion, so it's a better match.
You want to restrict the catch-all constructor to only be included in overload resolution if the arguments are suitable for constructing the std::array
member. This is a standard application of SFINAE.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.