Бесплатный оператор->* зло перегрузок?

Я просматривал раздел 13.5 после опровержения понятия, что встроенные операторы не участвуют в разрешении перегрузки и заметили, что нет никакого раздела по operator->*. Это - просто универсальный бинарный оператор.

Его братья, operator->, operator*, и operator[], все требуются быть нестатическими функциями членства. Это устраняет определение бесплатной функциональной перегрузки к оператору, наиболее часто используемому для получения ссылки из объекта. Но редкое operator->* не учтен.

В частности, operator[] имеет много общих черт. Это является двоичным (они упустили прекрасную возможность сделать это не), и это принимает некоторый контейнер слева и некоторый локатор справа. Его раздел специальных правил, 13.5.5, кажется, не имеет фактического эффекта кроме запретить бесплатные функции. (И то ограничение даже устраняет поддержку коммутативности!)

Так, например, это совершенно законно:

#include 
#include 
using namespace std;

template< class T >
T &
operator->*( pair &l, bool r )
    { return r? l.second : l.first; }

template< class T >
 T & operator->*( bool l, pair &r ) { return r->*l; }

int main() {
        pair y( 5, 6 );
        y->*(0) = 7;
        y->*0->*y = 8; // evaluates to 7->*y = y.second
        cerr << y.first << " " << y.second << endl;
}

Легко найти использование, но альтернативный синтаксис имеет тенденцию не быть этим плохо. Например, масштабированные индексы для vector:

v->*matrix_width[2][5] = x; // ->* not hopelessly out of place

my_indexer<2> m( v, dim ); // my_indexer being the type of (v->*width)
m[2][5] = x; // it is probably more practical to slice just once

Комитет по стандартам забывал предотвращать это, это считали слишком ужасным для беспокойства или является там реальными вариантами использования?

38
задан Community 23 May 2017 в 11:53
поделиться

3 ответа

Погуглив немного, я нашел больше случаев, когда люди спрашивают, используется ли operator->*, чем реальных предложений.

В нескольких местах предлагается T &A::operator->*( T B::* ). Не уверен, отражает ли это замысел дизайнера или ошибочное впечатление, что T &A::operator->*( T A::* ) является встроенным модулем. Не совсем относится к моему вопросу, но дает представление о глубине, которую я обнаружил в онлайн дискуссиях и литературе.

Там было упоминание о "D&E 11.5.4", что, как я полагаю, является Design and Evolution of C++. Возможно, это содержит подсказку. В противном случае, я просто собираюсь заключить, что это немного бесполезного уродства, которое было пропущено стандартизацией, да и большинством других тоже.

Edit См. ниже вставку цитаты из D&E.

Если говорить количественно, то ->* - это самый жесткий оператор связывания, который может быть перегружен свободной функцией. Все перегрузки постфиксных выражений и унарных операторов требуют нестатических сигнатур функций-членов. Следующими по старшинству после унарных операторов идут касты в стиле C, которые, можно сказать, соответствуют функциям преобразования (operator type()), которые также не могут быть свободными функциями. Далее идет ->*, затем умножение. ->* могло быть как [] или как %, они могли пойти по любому пути, и они выбрали путь EEEEEEVIL.

2
ответ дан 27 November 2019 в 03:55
поделиться

Я согласен с вами в том, что в стандарте есть несогласованность. Он не допускает перегрузки оператора [] функциями, не являющимися членами, и допускает его для оператора -> * . С моей точки зрения, operator [] относится к массивам, как operator -> * - к структурам / классам (геттер). Члены массива выбираются с помощью индекса. Члены структуры выбираются с помощью указателей на элементы.

Хуже всего то, что у нас может возникнуть соблазн использовать -> * вместо operator [] , чтобы получить массив, подобный element

int& operator->*(Array& lhs, int i);

Array a;

a ->* 2 = 10;

. Также возможна другая несогласованность. Мы можем использовать функцию, не являющуюся членом, для перегрузки operator + = и всех операторов формы @ = ), и мы не можем сделать это для operator = .

Я действительно не знаю, каково обоснование того, чтобы сделать следующее законным

struct X {
    int val;
    explicit X(int i) : val(i) {}
};
struct Z {
    int val;
    explicit Z(int i) : val(i) {}
};
Z& operator+=(Z& lhs, const X& rhs) {
    lhs.val+=rhs.val;
    return lhs;
}

Z z(2);
X x(3);
z += x;

и запрещающим

Z& operator=(Z& lhs, const X& rhs) {
    lhs.val=i;
    return lhs;
}

z = x;

Извините, что не отвечаю на ваш вопрос, но добавляю еще больше путаницы.

7
ответ дан 27 November 2019 в 03:55
поделиться

Стандарт (Рабочий проект 2010-02-16, § 5.5) гласит:

Результатом выражения -> * является lvalue , только если его второй операнд является указателем . члену данных. Если второй операнд является нулевым указателем на значение члена (4.11), поведение не определено.

Вы можете захотеть, чтобы это поведение было четко определенным . Например, проверьте, является ли это нулевым указателем, и обработайте эту ситуацию. Итак, я считаю, что стандарт разрешает перегрузку -> * - правильное решение.

1
ответ дан 27 November 2019 в 03:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: