C++ 在 struct/class 包装器中重载自动运算符
C++ overloading auto operator in struct/class wrapper
假设您有一个简单的 2D Point 对象,带有两个 setter 和 getters。
template <typename T>
class Point
{
public:
Point(T x, T y);
T getX() const;
T getY() const;
void setX(T x);
void setY(T y);
private:
T _x;
T _y;
};
但我想在更多 'scriptable-like' 语法中使用此 class。像 :
auto point = Point<double>(10, 10);
point.x = 20;
point.y = point.x + 10;
你会说,只需使用带有 public 变量的结构即可:
template <typename T>
struct Point
{
T x;
T y;
};
是的,但我想保留参数的隐私并用一些方法扩展 class。所以另一个想法是制作一个包装器助手,将运算符别名添加到 setters/getters :
template <typename T, typename Get, Get(T::*Getter)() const,
typename Set, void(T::*Setter)(Set)>
struct ReadWrite
{
ReadWrite(T& ptr) : ptr(ptr) {}
inline void operator= (Set const& rhs)
{
(ptr.*Setter)(rhs);
}
inline Get operator()()
{
return (ptr.*Getter)();
}
private:
T& ptr;
};
好的,我只是修改我的点 class 来完成工作:
template <typename T>
class Point
{
public:
Point(T x, T y);
T getX() const;
T getY() const;
void setX(T x);
void setY(T y);
private:
T _x;
T _y;
public:
ReadWrite<Point<T>, T, &Point<T>::getX, T, &Point<T>::setX> x;
ReadWrite<Point<T>, T, &Point<T>::getY, T, &Point<T>::setY> y;
};
通过添加一些算术运算符 ( + - * / ),我可以这样使用它:
auto point = Point<double>(10, 10);
point.x = 20;
point.y = point.x + 10;
在这里,point.x
在运算符重载的情况下是可以的:
template <typename T, typename V> inline T operator+(ReadWrite<T> const& lhs, V const& rhs) { return lhs() + rhs; }
template <typename T, typename V> inline T operator-(ReadWrite<T> const& lhs, V const& rhs) { return lhs() - rhs; }
template <typename T, typename V> inline T operator*(ReadWrite<T> const& lhs, V const& rhs) { return lhs() * rhs; }
template <typename T, typename V> inline T operator/(ReadWrite<T> const& lhs, V const& rhs) { return lhs() / rhs; }
如果我想使用这种语法,但 point.x
getter 上没有括号:
auto point = Point<double>(10, 10);
auto x = point.x();
我扩展了 ReadWrite 助手:
template <typename T, typename Get, Get(T::*Getter)() const,
typename Set, void(T::*Setter)(Set)>
struct ReadWrite
{
ReadWrite(T& ptr) : ptr(ptr) {}
inline void operator= (Set const& rhs)
{
(ptr.*Setter)(rhs);
}
inline Get operator()()
{
return (ptr.*Getter)();
}
inline operator auto() -> Get
{
return operator()();
}
private:
T& ptr;
};
现在没有括号:
double x = point.x; // OK, x is my x value (Point).
auto x = point.x; // Wrong, x is my ReadWrite<T> struct.
重载 auto
运算符有什么问题?
非常感谢您的回答。
你的class没有问题。问题是 auto 如何推导类型。关于 auto 你必须记住的一点是它基本上遵循模板参数推导的规则,主要的是不会进行隐式转换。这意味着在
auto x = point.x;
你说编译器,给我一个名为x
的变量,它具有初始化表达式的类型。在这种情况下,point.x
是一个 ReadWrite<Point<T>, T, &Point<T>::getX, T, &Point<T>::setX>
,所以这是 x
得到的类型。改变它的唯一方法是改变初始化表达式 returns.
很遗憾,我不确定您是怎么做到的。代理对象不能很好地使用自动类型推导,因为我们选择它们的类型,而不是它们正在模仿的类型。
假设您有一个简单的 2D Point 对象,带有两个 setter 和 getters。
template <typename T>
class Point
{
public:
Point(T x, T y);
T getX() const;
T getY() const;
void setX(T x);
void setY(T y);
private:
T _x;
T _y;
};
但我想在更多 'scriptable-like' 语法中使用此 class。像 :
auto point = Point<double>(10, 10);
point.x = 20;
point.y = point.x + 10;
你会说,只需使用带有 public 变量的结构即可:
template <typename T>
struct Point
{
T x;
T y;
};
是的,但我想保留参数的隐私并用一些方法扩展 class。所以另一个想法是制作一个包装器助手,将运算符别名添加到 setters/getters :
template <typename T, typename Get, Get(T::*Getter)() const,
typename Set, void(T::*Setter)(Set)>
struct ReadWrite
{
ReadWrite(T& ptr) : ptr(ptr) {}
inline void operator= (Set const& rhs)
{
(ptr.*Setter)(rhs);
}
inline Get operator()()
{
return (ptr.*Getter)();
}
private:
T& ptr;
};
好的,我只是修改我的点 class 来完成工作:
template <typename T>
class Point
{
public:
Point(T x, T y);
T getX() const;
T getY() const;
void setX(T x);
void setY(T y);
private:
T _x;
T _y;
public:
ReadWrite<Point<T>, T, &Point<T>::getX, T, &Point<T>::setX> x;
ReadWrite<Point<T>, T, &Point<T>::getY, T, &Point<T>::setY> y;
};
通过添加一些算术运算符 ( + - * / ),我可以这样使用它:
auto point = Point<double>(10, 10);
point.x = 20;
point.y = point.x + 10;
在这里,point.x
在运算符重载的情况下是可以的:
template <typename T, typename V> inline T operator+(ReadWrite<T> const& lhs, V const& rhs) { return lhs() + rhs; }
template <typename T, typename V> inline T operator-(ReadWrite<T> const& lhs, V const& rhs) { return lhs() - rhs; }
template <typename T, typename V> inline T operator*(ReadWrite<T> const& lhs, V const& rhs) { return lhs() * rhs; }
template <typename T, typename V> inline T operator/(ReadWrite<T> const& lhs, V const& rhs) { return lhs() / rhs; }
如果我想使用这种语法,但 point.x
getter 上没有括号:
auto point = Point<double>(10, 10);
auto x = point.x();
我扩展了 ReadWrite 助手:
template <typename T, typename Get, Get(T::*Getter)() const,
typename Set, void(T::*Setter)(Set)>
struct ReadWrite
{
ReadWrite(T& ptr) : ptr(ptr) {}
inline void operator= (Set const& rhs)
{
(ptr.*Setter)(rhs);
}
inline Get operator()()
{
return (ptr.*Getter)();
}
inline operator auto() -> Get
{
return operator()();
}
private:
T& ptr;
};
现在没有括号:
double x = point.x; // OK, x is my x value (Point).
auto x = point.x; // Wrong, x is my ReadWrite<T> struct.
重载 auto
运算符有什么问题?
非常感谢您的回答。
你的class没有问题。问题是 auto 如何推导类型。关于 auto 你必须记住的一点是它基本上遵循模板参数推导的规则,主要的是不会进行隐式转换。这意味着在
auto x = point.x;
你说编译器,给我一个名为x
的变量,它具有初始化表达式的类型。在这种情况下,point.x
是一个 ReadWrite<Point<T>, T, &Point<T>::getX, T, &Point<T>::setX>
,所以这是 x
得到的类型。改变它的唯一方法是改变初始化表达式 returns.
很遗憾,我不确定您是怎么做到的。代理对象不能很好地使用自动类型推导,因为我们选择它们的类型,而不是它们正在模仿的类型。