Используя станд.:: карта <K, V>, где V не имеет никакого применимого конструктора по умолчанию

Мне реализовали таблицу символов как a std::map. Для значения нет никакого способа законно создать экземпляр типа значения с помощью конструктора по умолчанию. Однако, если я не предоставляю конструктора по умолчанию, я получаю ошибку компилятора и если я заставляю конструктора утверждать, моя компиляция программы очень хорошо, но катастрофические отказы в map<K,V>::operator [] если я пытаюсь использовать его для добавления нового участника.

Есть ли способ, которым я могу заставить C++ запрещать map[k] как l-значение во время компиляции (при разрешении его как r-значения)?


BTW: Я знаю, что могу вставить в использование карты Map.insert(map<K,V>::value_type(k,v)).


Править: у нескольких человек есть предлагаемое решение, которые составляют изменение типа значения так, чтобы карта могла создать один, не называя конструктора по умолчанию. Это имеет точно противоположный результат того, что я хочу, потому что он скрывает ошибку до позже. Если бы я был готов иметь это, то я мог бы просто удалить утверждение от конструктора. То, что я Хочу, должно совершить ошибку, происходят еще раньше; во время компиляции. Однако кажется, что нет никакого способа различать r-значение и использование l-значения operator[] таким образом, кажется тем, что я хочу, не может быть сделан так, я должен буду просто обойтись без использования всего этого вместе.

42
задан BCS 21 December 2009 в 20:51
поделиться

8 ответов

Вы не можете заставить компилятор различать два использования оператора [], потому что это одно и то же. Operator [] возвращает ссылку, поэтому версия присваивания просто присваивается этой ссылке.

Лично я никогда не использую operator [] для карт для чего-либо, кроме быстрого и грязного демонстрационного кода. Вместо этого используйте insert () и find (). Обратите внимание, что функция make_pair () упрощает использование вставки:

m.insert( make_pair( k, v ) );

В C ++ 11 вы также можете выполнить

m.emplace( k, v );
m.emplace( piecewise_construct, make_tuple(k), make_tuple(the_constructor_arg_of_v) );

, даже если конструктор копирования / перемещения не предоставлен.

42
ответ дан 26 November 2019 в 23:54
поделиться

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

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

Например:

template <class K, class V>
V& get(std::map<K, V>& m, const K& k)
{
    typename std::map<K, V>::iterator it = m.find(k);
    if (it != m.end()) {
        return it->second;
    }
    throw std::range_error("Missing key");
}

template <class K, class V>
const V& get(const std::map<K, V>& m, const K& k)
{
    typename std::map<K, V>::const_iterator it = m.find(k);
    if (it != m.end()) {
        return it->second;
    }
    throw std::range_error("Missing key");
}

template <class K, class V>
void set(std::map<K, V>& m, const K& k, const V& v)
{
    std::pair<typename std::map<K, V>::iterator,bool> result = m.insert(std::make_pair(k, v));
    if (!result.second) {
        result.first->second = v;
    }
}

Вы также можете рассмотреть такой геттер, как dict.get (key [ , default]) в Python (который возвращает предоставленное значение по умолчанию, если ключ отсутствует (но при этом возникает проблема удобства использования в том, что значение по умолчанию всегда должно быть создано, даже если вы знаете, что этот ключ находится на карте).

4
ответ дан 26 November 2019 в 23:54
поделиться

Ваш V не имеет конструктора по умолчанию, поэтому вы не можете ожидать, что std :: map std :: map :: operator [] для использования.

A std :: map > имеет ли ] mapped_type , который может быть сконструирован по умолчанию и, вероятно, имеет желаемую семантику. За подробностями обращайтесь к документации Boost.Optional (вы должны знать о них).

5
ответ дан 26 November 2019 в 23:54
поделиться

Не уверен, почему он компилируется для вас, я думаю, компилятор должен был обнаружить ваш отсутствующий конструктор.

как насчет использования

map<K,V*>

вместо

map<K,V> ?
1
ответ дан 26 November 2019 в 23:54
поделиться

Когда вы используете переопределение оператора в C ++, лучше придерживаться семантики оператора как можно точнее. в случае по умолчанию. Семантика по умолчанию. operator [] заменяет существующий член в массиве. Казалось бы, std :: map немного меняет правила. Это прискорбно, поскольку приводит к путанице подобного рода.

Обратите внимание, что документация ( http://www.sgi.com/tech/stl/Map.html ) для оператора [] под std :: map говорит: «Возвращает ссылку на объект, связанный с конкретным ключом. Если карта еще не содержит такого объекта, operator [] вставляет объект по умолчанию data_type ()».

Я бы предложил что вы по-разному относитесь к замене и прошивке. К сожалению, это означает, что вам нужно знать, что требуется. Это может означать сначала поиск на карте. Если производительность является проблемой, вам может потребоваться найти оптимизацию, в которой вы можете проверить членство и вставить с помощью одного поиска.

0
ответ дан 26 November 2019 в 23:54
поделиться

вы можете специализировать std :: map для вашего типа значения. Я не говорю, что это хорошая идея, но это возможно. Я настроил dtor scoped_ptr на fclose вместо delete .

Что-то вроде:

 template<class K, class Compare, class Allocator>
 my_value_type& std::map<K,my_value_type,Compare,Allocator>::operator[](const K& k) 
 {
   //...
 }

Это должно позволить вам вставить код вы хотите в operator [] для вашего типа. К сожалению, в текущем C ++ я не знаю способа вернуть только r значений. В c ++ 0x вы можете использовать:

 template<class K, class Compare, class Allocator>
 my_value_type&& std::map<K,my_value_type,Compare,Allocator>::operator[](const K& k) 
 {
   //...
 }

Это вернет ссылку на R-значение (&&).

0
ответ дан 26 November 2019 в 23:54
поделиться

Это немного некрасиво, но один из способов обойти это - добавить переменную-член, которая отслеживает, действителен ли экземпляр или нет. Конструктор по умолчанию помечает экземпляр как недопустимый, но все остальные конструкторы помечают этот экземпляр как допустимый.

Убедитесь, что ваш оператор присваивания правильно передает новую переменную-член.

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

Измените все остальные функции-члены так, чтобы они генерировали / ошибались / утверждали, когда они работают с недопустимым экземпляром.

Затем вы можете использовать свой объект на карте, и пока вы используете только объекты, которые были правильно сконструированы, ваш код будет работать нормально .

Опять же, это обходной путь, если вы хотите использовать карту STL и не хотите использовать вставку и поиск вместо оператора [].

1
ответ дан 26 November 2019 в 23:54
поделиться

Получите новый класс из std :: map и создайте свой собственный оператор [] . Пусть он вернет ссылку на константу, которую нельзя использовать как l-значение.

3
ответ дан 26 November 2019 в 23:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: