struct / class包装中的C ++重载自动运算符

Max*_*its 3 c++ operator-overloading wrapper template-meta-programming auto

想象一下,您有一个简单的2D点对象,其中包含两个设置器和获取器。

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;
};
Run Code Online (Sandbox Code Playgroud)

但是我想以更“类似于脚本”的语法来使用此类。就像是 :

auto point = Point<double>(10, 10);
point.x = 20;
point.y = point.x + 10;
Run Code Online (Sandbox Code Playgroud)

您会说,只需使用带有公共变量的结构:

template <typename T>
struct Point
{
    T x;
    T y;
};
Run Code Online (Sandbox Code Playgroud)

是的,但是我想保留参数的私密性,并使用某些方法扩展类。因此,另一个想法是制作一个包装助手,将操作符别名添加到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;
};
Run Code Online (Sandbox Code Playgroud)

好的,我只是修改Point类来完成工作:

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;
};
Run Code Online (Sandbox Code Playgroud)

通过添加一些算术运算符(+-* /),我可以像这样使用它:

auto point = Point<double>(10, 10);
point.x = 20;
point.y = point.x + 10;
Run Code Online (Sandbox Code Playgroud)

这里,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; }
Run Code Online (Sandbox Code Playgroud)

如果我想使用这种语法,但是在point.xgetter 上没有括号:

auto point = Point<double>(10, 10);
auto x = point.x();
Run Code Online (Sandbox Code Playgroud)

我通过以下方式扩展了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;
}; 
Run Code Online (Sandbox Code Playgroud)

现在没有括号:

double x = point.x; // OK, x is my x value (Point).
auto x = point.x;   // Wrong, x is my ReadWrite<T> struct.
Run Code Online (Sandbox Code Playgroud)

auto操作员超负荷怎么了?

非常感谢您的回答。

Nat*_*ica 7

您的课程没有错。问题是如何自动推导类型。关于auto,您要记住的一点是,它基本上遵循模板参数推导的规则,而最主要的是不会进行隐式转换。这意味着

auto x = point.x;
Run Code Online (Sandbox Code Playgroud)

您说编译器,给我一个变量x,它具有初始化表达式的类型。在这种情况下point.xReadWrite<Point<T>, T, &Point<T>::getX, T, &Point<T>::setX>这样有类型x得到。更改的唯一方法是更改​​初始化表达式返回的内容。

不幸的是,我不确定您怎么做。代理对象在自动类型推断中不能很好地发挥作用,因为我们选择它们的类型,而不是它们模仿的对象。