我不知道如何描述模板的类型是结构本身,如下所示。
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
用于r1
和r2
,因此编译器会推断出类型。
同样,这取决于你是否认为这种行为对你的班级有意义。
这是定义的行为。要允许以递归方式使用,您需要在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);
}