对类似数学矢量的操作进行自动符号转换

时间:2017-07-18 10:34:20

标签: c++ templates type-conversion

我有一个自定义向量(在数学上下文中使用而不是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); }

};

现在我使用带有签名类型的typedef作为大小的位置和无符号类型。但我发现我需要经常将这些组合在一起,这会使转换变得麻烦。参见:

typedef Point<int> Pos;
typedef Point<unsigned> Extent;

Pos pos = ...;
Extent size = ...;
std::cout << "The outside is: " << (pos + Pos(size));

我想要的是,无符号类型会自动转换为有符号类型,但不能反过来,所以我只需编写pos + size
这很容易吗?

关于推理的注意事项:我可以组合有符号和无符号基类型,例如5 + 6u,这会导致 signed 无符号类型。 编辑:但组合一个位置(有符号)和一个范围(无符号)应该会产生一个新的位置。

如果自动转换仅适用于使用数学运算符,则更好。

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

3 个答案:

答案 0 :(得分:3)

您可以将operator+编写为免费函数,使用返回类型“添加两种类型的结果类型”,例如:

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只为我们做了一些类型演绎,auto返回类型operator+也为我们做了类型演绎(你可以使用{{1'手动“做” }和decltype - 构造)。你需要至少一个c ++ 14编译器才能工作(如果取消对尾随返回类型的注释,则需要c ++ 11)。当然,您可以对std::declvaloperator-等使用类似的代码。

答案 1 :(得分:3)

您需要以允许它接受具有不同类型参数的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重新定义错误。

答案 2 :(得分:0)

在@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