cpp-ru / ideas

Идеи по улучшению языка C++ для обсуждения
https://cpp-ru.github.io/proposals
Creative Commons Zero v1.0 Universal
89 stars 0 forks source link

Несколько аргументов в перегруженном brackets operator #235

Closed apolukhin closed 2 years ago

apolukhin commented 3 years ago

Перенос предложения: голоса +14, -5 Автор идеи: Олег Ляттэ

Это позволит использовать более лаконичные и понятные выражения для доступа к многомерным массивам и подобным структурам

Если возникает необходимость работы с многомерными структурами, то иногда приходится делать так (на примере матрицы трансформации):

class Matrix
{
public:
  float* operator[](int row);
};

Matrix m;
m[3][1] = 0.0f;

Мы используем operator[] с одним аргументом, который должен вернуть нечто (в данном случае указатель), к чему можно ещё раз применить operator[] с одним аргументом, и так до тех пор, пока не достигнется нужное количество измерений.

Хорошо, если это "нечто" - некий простой встроенный тип (ссылка на массив или указатель). А если это некая внутренняя структура, которую хотелось бы сохранить приватной? Приходится добавлять вспомогательные типы-хелперы, единственная задача которых - быть промежуточным звеном в цепочке квадратных скобок.

С предложенным изменением можно написать так:

class Matrix
{
public:
  float& operator[](int row, int column);
};

Matrix m;
m[3, 1] = 0.0f;

Здесь нет промежуточных типов, и не надо гадать, что происходит: один вызов оператора для получения нужного результата. А увеличение количества измерений приведёт лишь к появлению дополнительных аргументов оператора.

apolukhin commented 3 years ago

ru.night.beast, 30 октября 2017, 15:36 запись m[3, 1] сломает существующий код

dix75, 30 октября 2017, 15:55 ru.night.beast, Да сломает, но если вспомнить, что это в основном ошибочная запись

ru.night.beast, 30 октября 2017, 16:17 dix75, а что делать в ситуациях, когда запись не ошибочная?

Олег Ляттэ, 30 октября 2017, 17:14 ru.night.beast, можно пояснить, что именно сломается? Что-то не могу сообразить.

ru.night.beast, 30 октября 2017, 17:28 Олег Ляттэ, ну, в плюсах (если оператор "," не перегружен), результатом выражения "3, 1" будет int 1. Т.е. один аргумент, а не несколько. Сломается код, который использует "," при вычислении индекса []

Дмитрий, 30 октября 2017, 20:57 Для float& operator[](int row, int column); можно оставить такой вариант записи: m[3][1] = 0.0f; Запятая смотрелась бы лучше, но её уже задействовали под весьма сомнительную конструкцию, в которой следовало бы использовать ";". Теперь имеем ";" в for, но "," в while.

nenomius, 30 октября 2017, 22:20 ru.night.beast, такой код заслуживает быть сломаным.

ru.night.beast, 30 октября 2017, 22:54 Дмитрий, и как быть если у класса перегружен оператор с одним и с двумя параметрами?

Олег Ляттэ, 31 октября 2017, 15:10 ru.night.beast, как вариант - поменять семантику запятой в квадратных скобках на аналогичную той, что используется при передаче аргументов функции. Рискованное предложение, да, но вдруг комитет решит, что это изменение стоит того.

dix75, 1 ноября 2017, 12:58 ru.night.beast, Имеет смысл только в variadic template (хотя и весьма сомнительный), в остальных случаях это описка(ошибка)

ru.night.beast, 1 ноября 2017, 16:09 dix75, перегрузкой "," можно добиться, например, передачу в operator[] многомерного индекса. причем скобки могут использоваться не только для работы с массивами. еще до x11 делали лямбды, где [] применялось для lazy-evaluation. так что, если вы лично этим не пользуетесь, это не значит что все, ошибка, давайте срочно переписывать.

dix75, 9 ноября 2017, 18:29 ru.night.beast, Хотите сказать, что у вас много кода где вы используете перегрузку ',' для []. Если да то могу только посочувствовать. Использовать не стандартное поведение - это ваше решение, почему все должны от этого страдать. Можно найти и придумать огромное кол-во перегрузок удобных для разработчиков,но выбивающихся из стандартной логики (например ^ для возведения в степень), но это не повод, не вводить в язык новые вещи которые интуитивно понятные для всех

ru.night.beast, 10 ноября 2017, 9:52 dix75, я хочу сказать, что перегрузка запятой может иметь вполне осмысленное применение и привел несколько примеров. Не понимаю, когда это перегрузка запятой стала вдруг "не стандартным поведением". Если эти самые новые вещи ломают старые правила и при этом ничего не дают взамен, то как раз таки повод.

dix75, 14 ноября 2017, 14:56 Уважаемый ru.night.beast, вы правы, конечно operator , имеет смысл. Но в контексте его применения в квадратных скобках на первых порах меня бы удивило бы, возможно (точнее точно) я бы смог привыкнуть к такому поведению в проекте, но все же для большого количе-ва людей такое поведение не типично. Но если существовала такая же практика как для operator <<, то тут возражения нет. Да и еще по сути разницы в использовании operator , а не operator && или еще другого нет. Это просто синтаксический сахар

ru.night.beast, 14 ноября 2017, 16:29 dix75, дело вкуса. мне больше нравится вариант ifnull[_1, _2, _3] чем ifnull[_1 && _2 && _3] ну и про приоритеты операторов не стоит забывать.

Antervis, 31 октября 2017, 6:52 перегружаемый operator , "убивает" большинство propolsal'ов еще в зачаточном состоянии.

синтаксис a[b, c], как уже было отмечено, может сломать существующий код. Если же делать синтаксис a[b][c], то непонятно, что делать, если помимо [] с несколькими аргументами есть еще вариант с одним аргументом, который возвращает указатель/объект с перегруженным []/объект, конвертируемый в указатель. Можно конечно определить список приоритетов так, чтобы не ломался старый код.

Проще, всё-таки, решить проблему введением библиотечных многомерных range - адаптеров

post-increment, 31 октября 2017, 7:03 Antervis, поясните про "убивает" proposal. Что именно он "убивает"?

Идея писать [i][j] вместо предложенного [i,j] мне не нравится в том числе и по этой причине.

Antervis, 31 октября 2017, 13:55 post-increment, в том плане, что существует много предложений, которые не могут быть реализованы из-за потенциальной порчи существовующего кода, использующего operator ,

post-increment, 31 октября 2017, 6:57 Мне идея кажется интересной. Я опасаюсь, что могут возникнуть трудности при совместимости кода. в старом коде, например, может быть такое выражение:

int m[size];
m[foo(i),i] = 1;

и это будет пониматься компилятором как последовательные вычисления через оператор "," и последующий вызов m[i]. То есть сейчас есть эквивалентность для массивов

m[foo(i),i] = 10; <===> foo(i); m[i] =10;

В вашем предложении это уже потребует другого понимания от компилятора.

В защиту можно сказать что компилятор справляется с аргументами (). почему бы ему не справляться и с []?

Можно ввести правила которые позволят сохранить старый код вида m[foo(i),i] = 10; для, скажем, массивов величин и для классов, где есть оператор [] только с одним аргументом... Но вообще я бы рекомендовал приводить такой код к виду foo(i); m[i] =10; При этом нововведении компилятор как раз обругает подобный старый код как ошибку, так как оператор [] оператор для 2 аргументов не был определён.

im.einio, 31 октября 2017, 11:26 Вместо предложенной перегрузки оператора [] вы можете использовать перегрузку оператора ().

class Matrix
{
public:
    float& operator()(int row, int column);
};

Matrix m;
m(3, 1) = 0.0f;

Олег Ляттэ, 31 октября 2017, 14:56 im.einio, да, так безусловно можно, и как обход проблемы можно делать именно так. Но тогда запись 'm(3, 1)' будет больше похожа на вызов функции, чем на доступ к элементу множества.

Дмитрий, 2 ноября 2017, 12:25 В range-v3 этот вопрос решён достаточно легко (код сильно упрощён):

operator[](slice_bounds);

struct slice_bounds
{
    From from;
    To to;
};

collection[{0, 10}];

Аналогично можно и с матрицами поступить.

apolukhin commented 3 years ago

Работа над предложением уже идёт https://wg21.link/p2128

apolukhin commented 2 years ago

Приняли в C++23, на основе этого оператора получится сделать нормальгый mdspan