C ++ перегружает авто оператор в оболочке структуры / класса - PullRequest
3 голосов
/ 05 апреля 2019

Представьте, что у вас есть простой объект 2D 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;
};

Но я хочу работать с этим классом в более похожем на сценарии синтаксисе. Что-то вроде:

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

Вы скажете, просто используйте структуру с публичной переменной:

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

Да, но я хочу сохранить конфиденциальность параметров и расширить класс с помощью некоторых методов. Поэтому другая идея заключается в создании помощника-обертки, который добавляет псевдоним оператора в методы установки / получения:

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?

Большое спасибо за ваш ответ.

1 Ответ

7 голосов
/ 05 апреля 2019

В вашем классе нет ничего плохого.Проблема в том, как авто выводит типы.Что нужно помнить об auto, так это то, что он в основном следует правилам вывода аргументов шаблона, и главное в этом , неявное преобразование не будет выполнено .Это означает, что в

auto x = point.x;

Вы говорите, компилятор, дайте мне переменную с именем x, которая имеет тип выражения инициализации.В этом случае point.x - это ReadWrite<Point<T>, T, &Point<T>::getX, T, &Point<T>::setX>, так что получается тип x.Единственный способ изменить это - изменить то, что возвращает выражение инициализации.

К сожалению, я не уверен, как вы могли это сделать.Прокси-объекты не очень хорошо работают с автоматическим удержанием типов, поскольку мы подбираем их тип, а не то, что они имитируют.

...