#include <cstdio>
#include <iostream>
#include <string>
#include <vector>
#include <mutex>
#include <thread>
#include <chrono>

using namespace std;
using namespace std::chrono_literals;

template <typename T>
class MyVector
{
private:
    struct DataWrapper {
        vector<T>&      data_ref;
        mutex&          data_mutex_ref;
        const size_t    index;

        DataWrapper(vector<T>& dr, mutex& dmr, const size_t idx) : data_ref(dr), data_mutex_ref(dmr), index(idx) {}
        DataWrapper(const DataWrapper&) = default;
        ~DataWrapper() = default;
        DataWrapper& operator= (const DataWrapper&) = default;

        DataWrapper& operator= (const T& rhs);
        operator T () const;
    };

public:
    DataWrapper operator[] (const size_t index);

private:
    typedef lock_guard<mutex> lock_type;

    vector<T>   data;
    mutex       data_mutex;
};

template <typename T>
typename MyVector<T>::DataWrapper MyVector<T>::operator[] (const size_t index)
{
    if (index >= this->data.size()) {
        lock_type lock(this->data_mutex);
        if (0 != index) { this->data.resize(index << 1); }
        else { this->data.resize(1); }
    }

    return DataWrapper(this->data, this->data_mutex, index);
}

template <typename T>
typename MyVector<T>::DataWrapper& MyVector<T>::DataWrapper::operator= (const T& rhs)
{
    lock_type lock(this->data_mutex_ref);
    this->data_ref.at(this->index) = rhs;
    return *this;
}


template <typename T>
MyVector<T>::DataWrapper::operator T () const
{
    lock_type lock(this->data_mutex_ref);
    return this->data_ref.at(this->index);
}


struct MyData
{
    string  name;
    size_t  data;

    MyData() = default;
    MyData(const char* n, const size_t d) : name(n), data(d) {}
    string tostr() const { return ("name: " + name + "\ndata: " + to_string(data)); }
};


MyVector<MyData> buffer;

void read(const size_t total)
{
    for (size_t i = 0; i < total; ++i) {
        std::this_thread::sleep_for(50ms);
        MyData x = buffer[i]; // valid, calling casting operator
        // invalid, it will try to find a member function 'tostr' in MyVector<T>::DataWrapper but not in MyData
        // after casting the return type MyVector<T>::DataWrapper to MyData&
        //cout << buffer[i].tostr() << '\n';
        cout << static_cast<MyData&>(buffer[i]).tostr() << '\n';
    }
}

void write(const size_t total)
{
    for (size_t i = 0; i < total; ++i) {
        buffer[i] = MyData("MyData", i + 1);
        std::this_thread::sleep_for(50ms);
    }
}

int main()
{
    constexpr size_t TOTAL = 15;
    thread tw(write, TOTAL);
    thread tr(read, TOTAL);

    tw.join();
    tr.join();
    return 0;
}