【问题标题】:C++ overloading auto operator in struct/class wrapper结构/类包装器中的 C++ 重载自动运算符
【发布时间】:2019-04-05 19:51:05
【问题描述】:

假设您有一个简单的 2D Point 对象,其中包含两个 setter 和 getter。

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;
};

但我想以更“类似脚本”的语法使用这个类。类似的东西:

auto point = Point<double>(10, 10);
point.x = 20;
point.y = point.x + 10;

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

template <typename T>
struct Point
{
    T x;
    T y;
};

是的,但我想保留参数的隐私并使用一些方法扩展类。所以另一个想法是制作一个包装器帮助器,将操作符别名添加到 setter/getter 中:

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;
};

好的,我只是修改我的 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;
};

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

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 运算符重载有什么问题?

非常感谢您的回答。

【问题讨论】:

  • C++ 没有像 C# 这样的属性。没有真正超级棒的方法可以做到这一点。 Michael Litvin 使用Property 模板的答案接近了,q.v.:stackoverflow.com/questions/8368512/…
  • 注意:我使用了类似 Michael Litvin 的技术,以便在修改变量(变成类似于属性的方式)时设置断点。作为一种调试技术非常有用,但完成后我删除了代码脚手架。
  • @DanielLangr 在这种情况下不,因为它是一个转换运算符。返回类型本质上是函数的名称。 inline operator auto() -&gt; Get 和写inline operator Get() 是一样的
  • @NathanOliver 不知道,感谢您的澄清。
  • @MaximeCoorevits 不。我现在正在写一个答案来解释原因。只需一分钟。

标签: c++ operator-overloading wrapper template-meta-programming auto


【解决方案1】:

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

auto x = point.x;

你说编译器,给我一个名为x 的变量,它具有初始化表达式的类型。在这种情况下,point.xReadWrite&lt;Point&lt;T&gt;, T, &amp;Point&lt;T&gt;::getX, T, &amp;Point&lt;T&gt;::setX&gt;,所以这就是 x 的类型。改变它的唯一方法是改变初始化表达式返回的内容。

很遗憾,我不确定您是如何做到这一点的。代理对象不能很好地使用自动类型推断,因为我们选择了它们的类型,而不是它们正在模仿的类型。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-05-03
  • 1970-01-01
  • 1970-01-01
  • 2020-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多