ideas
ideas copied to clipboard
Добавить свойства в C++
При работе с классами, хорошей практикой, является объявление полей закрытыми. Для чтения значения полей из вне, в классе объявляют геттеры. А для записи объявляют сеттеры.
Возьмём для примера класс QPoint с фреймворка Qt.
class QPoint {
public:
// construct, dectructor, other
inline int x() const { return xp; }
inline int y() const { return yp; }
inline void setX(int x) { this.xp = x; }
inline void setY(int y) { this.yp = y; }
private:
int xp;
int yp;
};
https://code.woboq.org/qt5/qtbase/src/corelib/tools/qpoint.h.html
При этом в большинстве случаев, геттеры и сеттеры очень простые. И не хочется писать руками.
Для того, чтобы каждый раз не писать геттеры, сеттеры, ничего не мешает реализовать оберточки.
template<typename T>
class Property {
public:
constexpr explicit Property(const T& value) : value(value) {}
constexpr Property& operator=(const T& newValue) {
this->value = newValue;
return *this;
}
constexpr operator const T&() const { return value; }
private:
T value;
};
class QPoint {
public:
// construct, dectructor, other
Property<int> x;
Property<int> y;
};
https://github.com/klappdev/firearrow/blob/master/app/src/main/cpp/util/property/Property.hpp https://github.com/klappdev/firearrow/blob/master/app/src/main/cpp/util/property/Getter.hpp https://github.com/klappdev/firearrow/blob/master/app/src/main/cpp/util/property/Setter.hpp
Концепция свойств(геттеров, сеттеров) существует во многих языках, таких например как Swift, Kotlin, C# и д.р., на уровне языка. Хочется что б в С++ завезли такую возможность на уровне языка. Например, это могло выглядить как в С#.
class QPoint {
public:
// construct, dectructor, other
private:
int x { set; get; };
int y { set; get; };
};
Предлагаю добавить два контекстных ключевых слова - set, get. Которые были бы ключевыми словами, только при объявлении поля класса. По аналогии с обычными методам, можно бы объявлять с разными квалификаторами - &, &&, const, volatile, noexcept. В случае если нужно не тривиальный геттер, сеттер, то код мог бы выглядеть следующим образом.
class QPoint {
public:
// construct, dectructor, other
private:
int x {
set(int value) {
std::cout << "invoke setter - x" << std::endl;
this->x = value;
};
get() const {
std::cout << "invoke getter - x" << std::endl;
return x;
};
};
int y { set; get; };
};
Под капотом, компилятор генерировал те самые методы геттеры, сеттеры. Современный С++ вносит много новых фич, которые позволяет писать меньше шаблонного кода. Концепция свойств(геттеров, сеттеров) как раз позволяет писать меньше кода, и при этом код остается таким же читаемым. Очень хочется видеть в С++.
Полезные ссылки:
- https://docs.microsoft.com/ru-ru/dotnet/csharp/properties
- https://kotlinlang.org/docs/properties.html
- https://docs.swift.org/swift-book/LanguageGuide/Properties.html
tl;dr: пример и аргументация кажутся неудачными, идею критике подвергать пока не буду.
При работе с классами, хорошей практикой, является объявление полей закрытыми. Возьмём для примера класс QPoint с фреймворка Qt.
Спорный переход: QPoint больше похож на структуру данных (внешний функционал), которую Qt почему-то реализовал как класс. Получился "квазикласс" в терминах этой статьи: http://www.idinews.com/quasiClass.pdf
Концепция свойств(геттеров, сеттеров) существует во многих языках, таких например как Swift, Kotlin, C# и д.р., на уровне языка.
Заметим, что во многих не менее замечательных языках концепция свойств годами отсутствует.
Концепция свойств(геттеров, сеттеров) как раз позволяет писать меньше кода, и при этом код остается таким же читаемым.
Если полей в структуре немного, то и кода для автоматических геттеров/сеттеров нужно немного, особенно с наличием auto
и decltype
.
Если полей в структуре много... то с ней чего-то не то.
class QPoint {
public:
// construct, dectructor, other
private:
int x {
set(int value) {
std::cout << "invoke setter - x" << std::endl;
this->x = value;
};
get() const {
std::cout << "invoke getter - x" << std::endl;
return x;
};
};
int y { set; get; };
};
В этом примере следующий шаг, который хочется сделать — применить этот геттер/сеттер к y
. В этот момент естественно ввести класс QCoordinate
, в котором определить operator=
и operator int()
.
class QPoint {
public:
// construct, dectructor, other
private:
int x { set; get; };
int y { set; get; };
};
Много вопросов, как это будет сочетаться с конструкциями, специфичными для C++.
Раз:
QPoint p;
std::vector<int*> pointers;
pointers.push_back(&p.x); // что это?
Два:
// что будет делать такой код?
auto pointer_to_member = &QPoint::x;
p.*pointer_to_member = 3;
Три:
QPoint::QPoint(int a, int b) : x(a), y(b) { } // вызывать ли сеттер?
Старые предложения об автоматических геттерах/сеттерах и более общем случае неявных функций, все не позднее 2005 года:
- PME: Properties, Methods and Events: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1384.pdf
- C++/CLI Properties http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1600.html (сразу говорят, что не идёт в стандарт C++)
- Implicitly-Callable Functions: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1611.pdf
- C++ Properties -- a Library Solution: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1615.pdf
В последнем предложении содержится много критики к решениям уровня языка
Геттеры/сеттеры на уровне языка работают как буто простые методы. Поэтому не должно конфликтовать с другими частями языка. В С++23 добавили deducing this. Что является тоже пересмотром методов в частности. И позволяет меньше писать шаблонного кода. Поэтому почему не пересмотреть и в сторону свойств.
Тривиальные геттеры и сеттеры это ошибка проектирования и бесполезный код. В джаву С++ не нужно превращать Сеттер должен делать что-то осмысленное и называться не SetX, например vector::resize/reserve, это вообще говоря в ваших терминах сеттеры, но называются они осмысленно и делают логичные для инкапсуляции логики вещи P.S. в расте есть vec::set_len просто ору
Вообще геттеры и сетеры очень удобны если нужно автоматически оповещать об изменении в данных. И на C++ довольно не удобно получать указатель на this класса, который содержит подобные геттеры и сеттеры.