Daily bit(e) C++. C++23: многомерный оператор индекса

Добавлено 4 августа 2023 в 02:55

Daily bit(e) C++ #4, многомерный operator[] из C++23, также известный как оператор индекса.

Daily bit(e) C++. C++23: оператор многомерного индекса

C++23 внес значительные изменения в оператор индекса. Теперь этот оператор может принимать несколько аргументов, разделенных запятой.

Раньше это было невозможно, так как до C++20 в индексах можно было использовать оператор запятой.

int v = some_array[1,2]; // до C++20, эквивалентно v = some_array[2];

Однако в C++20 использование оператора запятой в индексах было объявлено устаревшим, а затем удалено в C++23.

После этого, теперь оператор индекса может принимать несколько аргументов:

#include <string>
#include <ranges>
#include <iostream>

struct Maze 
{
    // operator[] теперь может принимать несколько аргументов
    char& operator[](size_t row, size_t col) 
    {
        if (row*width+col >= data.length())
            throw std::out_of_range("out of bounds access");
        return data[row*width+col];
    }
    const char& operator[](size_t row, size_t col) const 
    {
        if (row*width+col >= data.length())
            throw std::out_of_range("out of bounds access");
        return data[row*width+col];
    }

    size_t height;
    size_t width;
    std::string data;
};

Maze maze{4,4,"# ### ###   ####"};

for (auto ridx : std::views::iota(0, 4)) 
{
    for (auto cidx : std::views::iota(0, 4)) 
    {
        std::cout << maze[ridx,cidx];
    }
    std::cout << "\n";
}

При таком подходе теперь можно представлять сложные структуры данных без использования прокси-объектов:

#include <iostream>
#include <utility>
#include <unordered_map>

// Очень простое (и не очень эргономичное)
// трехмерное хранилище
struct Sparse3D 
{
    // Изменяющий доступ, всегда создает ячейку
    int64_t& operator[](int64_t x, int64_t y, int64_t z) 
    {
        return store_[x][y][z];
    }
    // способ константного доступа, только читает
    int64_t operator[](int64_t x, int64_t y, int64_t z) const 
    {
        auto xi = store_.find(x);
        if (xi == store_.end()) return empty;
        auto yi = xi->second.find(y);
        if (yi == xi->second.end()) return empty;
        auto zi = yi->second.find(z);
        if (zi == yi->second.end()) return empty;
        return zi->second;
    }
private:
    std::unordered_map<int64_t, 
        std::unordered_map<int64_t, 
            std::unordered_map<int64_t, 
                int64_t>>> store_;
    int64_t empty = 0;
};

Sparse3D data;
// создает ячейку и сохраняет в ней 20
data[0,0,0] = 20; 
// читает не существующую ячейку без ее создания
std::cout << std::as_const(data)[0,1,2] << "\n";
// печатает: 0
std::cout << std::as_const(data)[0,0,0] << "\n";
// печатает: 20

В качестве побочного эффекта этого изменения оператор индекса теперь ведет себя точно так же, как оператор вызова, предоставляя нам новый и странный способ сделать объект вызываемым.

// operator[] теперь ведет себя точно так же,
// как operator(), приводя к некоторым
// "интересным случаям использования"
struct WeirdCallable 
{
    void operator()(const std::string& what, const std::string& where) 
    {
        std::cout << "I will " << what << " you at the " << where << ".\n";
    }
    void operator[](const std::string& what, const std::string& where) 
    {
        std::cout << "You will " << what << " me at the " << where << ".\n";
    }
};

// Возможно, что-то следует запретить
// в вашем местном руководстве по стилю
WeirdCallable me;
me("greet","market");
// печатает: "I will greet you at the market."
me["hug","crossroad"];
// печатает: "You will hug me at the crossroad."

Открыть пример на Compiler Explorer.

Теги

C++ / CppC++23Daily bit(e) C++Оператор индексаПрограммирование

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.