自动符号转换,用于对类等数学向量进行操作

Automatic sign conversion for operations on math-vector like class

本文关键字:向量 操作 符号 转换 用于      更新时间:2023-10-16

我有一个自定义向量(用于数学上下文而不是std::vector(,它在元素类型上模板化。 它还提供从其他类型的显式转换。

总之:

template <typename T>
struct Point
{
T x, y;
Point(const T x, const T y): x(x), y(y) {}
Point(const Point& other): x(other.x), y(other.y){}
template<typename U>
explicit Point(const Point<U>& pt): x(static_cast<T>(pt.x)), y(static_cast<T>(pt.y)) {}
Point& operator+=(const Point& right);
friend Point operator+(Point left, const Point& right) { return (left+=right); }
};

现在我使用带有符号类型的 typedefs 作为职位,使用无符号类型表示大小。但是我发现我需要经常将它们结合起来,这在转换中变得很麻烦。看:

typedef Point<int> Pos;
typedef Point<unsigned> Extent;
Pos pos = ...;
Extent size = ...;
std::cout << "The outside is: " << (pos + Pos(size));

我想要的是无符号类型会自动转换为有符号类型,而不是相反,所以我可以简单地编写pos + size.
这容易吗?

推理说明:我可以组合有符号和无符号的基本类型,如5 + 6u,从而产生

有符号的无符号类型。 编辑:但是将职位(已签名(和范围(未签名(结合起来应该会产生一个新职位。

更好的是,如果自动转换仅在使用数学运算符时才适用。

编辑:仅限C++98,但允许提升

您可以将operator+编写为 free 函数,该函数用作返回类型"添加两种类型的结果类型",例如:

template <typename T>
Point<T> make_point(T x, T y)
{
return Point<T>(x, y);
}

template <typename T, typename U>
auto operator+(Point<T> const & lhs, Point<U> const & rhs)
// -> decltype(make_point(lhs.x + rhs.x, lhs.y + rhs.y))
{
return make_point(lhs.x + rhs.x, lhs.y + rhs.y);
}

其中make_point只是为我们做一些类型推断,operator+auto返回类型也为我们做类型推断(您可以使用decltypestd::declval-构造"手动"完成(。您至少需要一个 c++14 编译器才能正常工作(如果您取消注释尾随返回类型,则为 c++11(。当然,您可以将类似的代码用于operator-operator*等。

您需要声明重载operator+,以允许它接受具有不同类型参数Point<>类。

其次,您可以依靠std::common_type在两者之间选择正确的结果类型。 即避免缩小转换的常见算术类型:

template <typename T>
struct Point
{
// as before, including your original operator+
template<typename S, typename U,
typename std::enable_if<!std::is_same<S, U>::value>::type* = nullptr, 
typename ResType = typename std::common_type<S, U>::type>
friend Point<ResType> operator+(const Point<S>& left, const Point<U>& right) {
return Point<ResType>{left} + Point<ResType>{right};
}
};

中间略微复杂的enable_if是一种SFINAE技术,用于避免SU相同时的GCC重定义错误。

在@MadScientiest和@StoryTeller的答案的基础上,我使用了以下内容:
首先是一个特征来获得结果类型

template<typename T>
struct TryMakeSigned
{
typedef typename boost::conditional<
boost::is_integral<T>::value,
boost::make_signed<T>,
boost::common_type<T>
>::type::type type;
};
/// Creates a mixed type out of types T and U which is
/// the common type of T & U AND signed iff either is signed
/// fails for non-numeric types with SFINAE
template<typename T, typename U, bool T_areNumeric = boost::is_arithmetic<T>::value && boost::is_arithmetic<U>::value>
struct MixedType;
template<typename T, typename U>
struct MixedType<T, U, true>
{
typedef typename boost::common_type<T, U>::type Common;
// Convert to signed iff least one value is signed
typedef typename boost::conditional<
boost::is_signed<T>::value || boost::is_signed<U>::value,
typename TryMakeSigned<Common>::type,
Common
>::type type;
};

第二次用于非成员运营商:

template <typename T, typename U>
inline Point<typename detail::MixedType<T, U>::type> operator+(const Point<T>& lhs, const Point<U>& rhs)
{
typedef typename detail::MixedType<T, U>::type Res;
return Point<Res>(lhs.x + rhs.x, lhs.y + rhs.y);
}

但是,这不考虑隐式转换。让那些将函数声明为类中的友元函数(省略typename T

(