Тип C ++, связанный с классами - PullRequest
0 голосов
/ 04 июля 2018

Я пишу некоторый код C ++, который оборачивает тип std :: unordered_map, где я хочу скрыть базовый тип и представить его как другой тип. Более конкретно, я хочу обернуть std :: pair из std :: unordered_map другим типом. Ради аргумента, давайте предположим, что оболочка выглядит следующим образом ...

template <typename ActualT >
class wrapper final
{
private:
    ActualT actual_;
public:
    //Some constructors...
    typename ActualT::first_type & get_first()
    {
        return actual_.first;
    }
    typename ActualT::second_type & get_second()
    {
        return actual_.second;
    }
};

Мое рассуждение состоит в том, что, поскольку у класса-обертки есть только член, который является точным типом, который он переносит, преобразование ссылки из исходного типа в тип обертки должно быть в порядке, но совместимость типов для структур гласит, что члены должен иметь одинаковый тип и имя для совместимости типов. Может ли использование подобного типа вызывать неопределенное поведение или проблемы с выравниванием?

using my_map = std::unordered_map < int, int >;
my_map m;
//Do some inserts...
reinterpret_cast<wrapper<typename my_map::value_type>&>(*m.find(10)).get_second() = 1.0;

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

Возможно, есть лучший подход для достижения этой цели?

Ответы [ 2 ]

0 голосов
/ 04 июля 2018

Это абсолютно неопределенное поведение.

Серьезно переосмыслите свои приоритеты.

Некоторые бесплатные функции вида

const my_map::key_type & MeaningfulNameHere(my_map::reference)

будет иметь большое значение для предоставления вам значимых имен.

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

template <typename Map>
class entry final
{
private:
    typename Map::reference ref;
public:
    entry(Map::reference ref) : ref(ref) {}

    const typename Map::key_type & key()
    {
        return ref.first;
    }
    typename Map::mapped_type & value()
    {
        return ref.second;
    }
};

Если вам действительно нужен , то итератор для разыменования на entry вы можете. Но вы можете просто неявно создавать entry s из Map::reference s, возвращаемых Map::iterator::operator*, вам не нужен пользовательский итератор.

template <typename Map>
class entry_iterator
{
private:
    typename Map::iterator it;
    entry<Map> entry;
public:
    entry<Map>& operator*() { return entry; }
    entry_iterator operator++() { ++it; entry = *it; return *this; }
    // etc
}
0 голосов
/ 04 июля 2018

Таким образом, вы могли бы убрать это, но я бы не советовал:

#include <unordered_map>
#include <iostream>
using namespace std;

template <class Key, class Value>
class wrapper
{
public:
   explicit wrapper(std::pair<const Key, Value>& kvp)
      : _key{kvp.first}
      , _value{kvp.second}
   {}

   const Key& key() const { return _key; }
   Value& value()         { return _value; }

private:
   const Key& _key;
   Value& _value;
};

int main()
{
   unordered_map<int,int> m;

   m[1] = 1;
   m[3] = 3;

   auto it = m.find(1);

   wrapper w{*it};
   w.value() = 30;

   std::cout << w.key() << " -> " << w.value() << '\n';
}

Вышесказанное эффективно скрывает pair от пользователей вашего класса. Он не работает с исключениями (например, find() возвращает end()) и не дает никаких гарантий относительно продолжительности жизни. Это немного лучше, чем у вас, потому что не требует reinterpret_cast для несвязанного типа.

Однако map, unordered_map, set и т. Д. Хранение возвращаемых итераторов в виде пар является лишь частью библиотеки - это каноническая форма, и я не вижу выгоды от ее защиты от людей.

...