我有一个自定义向量(在数学上下文中使用而不是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,但允许提升
答案 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::declval
,operator-
等使用类似的代码。
答案 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技术,可以在S
和U
相同时避免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
)