如何使用common_type和模板递归类型重载运算符+



我不知道如何描述模板的类型是结构本身,如下所示。

template<typename T> struct Point{};
Point<Point<int>> p;

这是定义的行为吗?如果是这样,我不知道实现它的最佳方法,以便我可以返回一个common_type而不会出错,如下所示。

#include <iostream>
template<typename T> struct Point
{
Point() {}
template<typename U, typename V> Point(const U& u, const V& v): x(u), y(v) {}
T x,y;
};
template<typename T, typename U> 
inline Point<typename std::common_type<T, U>::type> operator+(const Point<T>& p, const U& n)
{ 
return {p.x+n, p.y+n};
}
int main() {
Point<int> p;
Point<double> r1 = p + 1.5; //works
Point<Point<int>> p2;
Point<Point<double>> r2 = p2 + 1.5; //error
return 0;
}

错误是:
no match for ‘operator+’ (operand types are ‘Point<Point<int> >’ and ‘double’)

如果您希望它起作用(在我看来不应该,但这取决于您(,您可以使用decltype(std::declval<T>()+std::declval<U>())而不是std::common_type<...>

#include <iostream>
template<typename T> struct Point
{
Point(): x{}, y{} {}
template<typename U, typename V> Point(const U& u, const V& v): x(u), y(v) {}
T x,y;
};
template<typename T, typename U> 
inline Point<decltype(std::declval<T>()+std::declval<U>())> operator+(const Point<T>& p, const U& n)
{ 
return {p.x+n, p.y+n};
}
template<typename T>
std::ostream &operator<<(std::ostream& os, const Point<T>& p)
{
os << "Point<" << typeid(T).name() << ">(x=" << p.x << ", y=" << p.y << ")";
return os;
}
int main() {
Point<int> p;
auto r1 = p + 1.5;
Point<Point<int>> p2;
auto r2 = p2 + 1.5;
std::cout << p << "n";
std::cout << r1 << "n";
std::cout << p2 << "n";
std::cout << r2 << "n";
return 0;
}

我还添加了一个重载打印出一个点。由于C++标准无法保证typeid(T).name()会提供什么,您可能会看到不同的东西,但这就是我得到的:

Point<i>(x=0, y=0)
Point<d>(x=1.5, y=1.5)
Point<5PointIiE>(x=Point<i>(x=0, y=0), y=Point<i>(x=0, y=0))
Point<5PointIdE>(x=Point<d>(x=1.5, y=1.5), y=Point<d>(x=1.5, y=1.5))

Point<i>Point<int>Point<d>Point<double>Point<5PointIiE>Point<Point<int>>Point<5PointIdE>Point<Point<double>>。请注意,我将auto用于r1r2,因此编译器会推断出类型。

同样,这取决于你是否认为这种行为对你的班级有意义。

这是定义的行为。要允许以递归方式使用,您需要在Point类上专门化std::common_type元函数。请注意,通常,在std命名空间中定义任何内容都会导致未定义的行为 - 但是,该标准专门允许您为自定义类型的某些std模板提供专用化。

#include <iostream>
template<typename T> struct Point
{
Point() {}
template<typename U, typename V> Point(const U& u, const V& v): x(u), y(v) {}
T x,y;
};
// provide custom common_type implementation
namespace std {
template <class T, class U>
struct common_type<Point<T>, U> {
using type = Point<typename std::common_type<T, U>::type>;
};
}
template<typename T, typename U>
inline Point<typename std::common_type<T, U>::type> operator+(const Point<T>& p, const U& n) {
return {p.x+n, p.y+n};
}
int main() {
Point<int> p;
Point<double> r1 = p + 1.5; //works
Point<Point<int>> p2;
Point<Point<double>> r2 = p2 + 1.5; //works
Point<Point<Point<int>>> p3;
Point<Point<Point<double>>> r3 = p3 + 1.5; //works
// and so on
return 0;
}

这不起作用,因为Point<anything>和数字类型之间没有通用类型。当然,你应该问问自己,你是否希望它开始工作,以及你是否不应该明确地处理一个级别的嵌套并停止在那里。

为了支持任意嵌套,你想要的是一个特例,当Point包含另一个Point时:这些然后被"剥离",因为Point<Point<T>, U>的结果类型被声明为添加Point<T>U的结果。

最后,我想您假设std::common_type<T,U>的结果与T{}+U{}相同,因此通过Point间接寻址进行的加法就像大多数内部字段上的加法一样工作。只要您强制编译器检查该事实,我就没有问题。

#include <type_traits>
template<typename T> struct Point
{
constexpr Point() {}
template<typename U, typename V> constexpr Point(const U& u, const V& v): x(u), y(v) {}
T x = {}, y = {};
};
template<typename T, typename U> 
inline constexpr Point<typename std::common_type_t<T, U>> operator+(const Point<T>& p, const U& n)
{
static_assert(std::is_same_v<std::common_type_t<T,U>, decltype(std::declval<T>() + std::declval<U>())>);
return {p.x+n, p.y+n};
}
template<typename T, typename U>
inline constexpr Point<decltype(Point<T>{} + U{})> operator+(const Point<Point<T>>& p, const U& n)
{
return {p.x+n, p.y+n};
}
template <class T, class U>
inline constexpr bool operator==(const Point<T> &p, const Point<U> &r)
{
return p.x == r.x && p.y == r.y;
}
int main() {
constexpr Point<int> p;
constexpr Point<double> r1 = p + 1.5;
constexpr Point<Point<int>> p2;
constexpr Point<Point<double>> r2 = p2 + 1.5;
constexpr Point<Point<Point<int>>> p3;
constexpr Point<Point<Point<double>>> r3 = p3 + 1.5;
constexpr Point<Point<Point<Point<int>>>> p4;
constexpr Point<Point<Point<Point<double>>>> r4 = p4 + 1.5;
static_assert(r4.x == p4.x + 1.5);
static_assert(r4.x.x == p4.x.x + 1.5);
static_assert(r4.x.x.x == p4.x.x.x + 1.5);
static_assert(r4.x.x.x.x == p4.x.x.x.x + 1.5);
}

最新更新