Перегрузить operator[] для встроенных типов, от своих типов

Я пытаюсь добавить немного типобезопасности и создаю тип:

struct Number
{
    explicit Number(int v) : value(v) {}
    explicit operator int() { return value; }

    // ... //

private:
    int value;
};

Затем, я хочу добавить возможность доступа к элементу массива по его "номеру" Number:

template <class T>
T& operator[](T* arr, Number i) { return arr[(int)i]; }

Однако определять operator[] вне типа запрещено стандартом.

Есть ли обходной путь сделать данный оператор, кроме определения собственного типа массива?


Ответы (3 шт):

Автор решения: Ildar

типы для индексации зашиты там намертво, думаю можно писать только свои велосипеды.


возможные варианты решения по моему мнению:

  1. наследоваться от нужного контейнера и расширить функционал(плохая идея, так как контейнеры стандартной библиотеки не имеют виртуальных деструкторов)
  2. писать свой контейнер с нужным функционалом
  3. скопировать реализацию нужного контейнера и изменить в нем необходимые методы(рассматривайте это как шуточный вариант, так как этот вариант не сочетается ни с какими идиомами)
  4. композиция, поместить вектор в какой нибудь класс, и сделать свой интерфейс, вызывая методы vector

небольшой пример для (1) случая, наследуемся от vector и добавляем operator[] принимающий Number в качестве индекса

#include <iostream>
#include <vector>

struct Number
{
  explicit Number(const int v) : value(v) {}
  explicit operator int() const { return value; }

  // ... //

private:
  int value;
};

template <typename T>
class ExtendedVector : public std::vector<T>
{
public:
  _NODISCARD T& operator[](const Number& pos) noexcept
  {
    return std::vector<T>::operator[](int(pos));
  }

  _NODISCARD const T& operator[](const Number& pos) const noexcept
  {
    return std::vector<T>::operator[](int(pos));
  }
};



int main()
{
  ExtendedVector<int> values;
  values.push_back(3);
  values.push_back(2);
  values.push_back(4);

  std::cout << values[Number(0)] << '\n';
  std::cout << values[Number(1)] << '\n';
  std::cout << values[Number(2)] << '\n';

  return 0;
}

как по мне это очень грустно выглядит и нужно определить кучу конструкторов, которые будут отправлять аргументы в конструкторы родителя-vector и еще кучу всего чтобы этот контейнер хотя бы работал как vector

→ Ссылка
Автор решения: dIm0n

Можно просто убрать explicit у оператора преобразования:

#include <iostream>

struct Number {
    explicit Number(int v) : value(v) {}
    operator int() { return value; }

    // ... //

private:
    int value;
};

int main() {
    int arr[] = {1, 2, 3};

    std::cout << arr[Number(1)] << '\n';
}

Или оставить explicit operator int, но сделать оператор преобразования для std::size_t без explicit.

→ Ссылка
Автор решения: Maxim Timakov

Можно подойти с другой стороны: добавить к Number шаблонный метод th_element_of с семантикой "N-й элемент контейнера"

class Number
{
template <typename Item>
Item& th_element_of(Item* items)
{
    return items[value];
}
};
SomeType items[];

//...
Number n = ....

auto item = n.th_element_of(items);

название метода подобрать по вкусу

→ Ссылка