Почему для аргумента типа карты C ++ требуется пустой конструктор при использовании []?

99

См. Также стандартный список C ++ и типы, которые можно построить по умолчанию.

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

#include <map>

struct MyClass
{
    MyClass(int t);
};

int main() {
    std::map<int, MyClass> myMap;
    myMap[14] = MyClass(42);
}

Это дает мне следующую ошибку g ++:

/usr/include/c++/4.3/bits/stl_map.h:419: ошибка: нет соответствующей функции для вызова 'MyClass ()'

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

Ник Болтон
источник
Приведенный выше код отлично компилируется на MinGW (g ++ 3.4.5) и MSVC ++ 2008 при условии, что для MyType задан typedef и в конце класса добавлена ​​точка с запятой. Вы, должно быть, делаете что-то еще (например, вызываете оператора [], как указано bb) - пожалуйста, опубликуйте полный код.
j_random_hacker
Ах да, ты прав. Сделаю.
Ник Болтон,
Да, без использования myMap вы не знаете, что нужно скомпилировать для класса карты. Какой поставщик и версия библиотеки stl тоже могут помочь.
Грег Домьян,

Ответы:

165

Эта проблема связана с оператором []. Цитата из документации SGI:

data_type& operator[](const key_type& k)- Возвращает ссылку на объект, связанный с определенным ключом. Если карта еще не содержит такого объекта, operator[] вставляет объект по умолчанию data_type().

Если у вас нет конструктора по умолчанию, вы можете использовать функции вставки / поиска. Следующий пример отлично работает:

myMap.insert( std::map< int, MyClass >::value_type ( 1, MyClass(1) ) );
myMap.find( 1 )->second;
байда
источник
11
Отличный ответ - обратите внимание также emplaceна C ++ 11 как краткую альтернативу insert.
prideout
3
Почему это std::<map>::value_typeесть в insertзвонке?
thomthom
1
Почему конструктор по умолчанию должен определяться пользователем?
Schuess
@schuess Я не вижу причин, почему это так: = defaultдолжно работать нормально.
underscore_d
Условие «Карта еще не содержит такого объекта» будет оцениваться во время выполнения. Почему ошибка времени компиляции?
Гаурав Сингх
7

Да. Значения в контейнерах STL должны поддерживать семантику копирования. IOW, они должны вести себя как примитивные типы (например, int), что означает, среди прочего, что они должны быть сконструированы по умолчанию.

Без этого (и других требований) было бы излишне сложно реализовать различные внутренние операции копирования / перемещения / замены / сравнения структур данных, с которыми реализованы контейнеры STL.

После ссылки на стандарт C ++ я вижу, что мой ответ был неточным. По сути, конструкция по умолчанию не является требованием :

Начиная с 20.1.4.1:

Конструктор по умолчанию не требуется. Определенные сигнатуры функций-членов класса контейнера указывают конструктор по умолчанию в качестве аргумента по умолчанию. T () должно быть четко определенным выражением ...

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

Реальные требования (23.1.3) для всех значений, хранящихся в контейнерах STL: CopyConstructibleи Assignable.

Существуют также другие особые требования к конкретным контейнерам, такие как Comparableналичие (например, для ключей на карте).


Между прочим, следующее компилируется без ошибок при комо :

#include <map>

class MyClass
{
public:
    MyClass(int t);
};

int main()
{
    std::map<int, MyClass> myMap;
}

Так что это может быть проблема g ++.

Ассаф Лави
источник
2
Как вы думаете, BB может что-то сказать относительно оператора []?
Ник Болтон
13
Этот код, вероятно, компилируется, потому что вы не вызываете myMap []
jfritz42
3

Проверьте требования хранимого типа stl :: map. Многие коллекции stl требуют, чтобы хранимый тип содержал определенные свойства (конструктор по умолчанию, конструктор копирования и т. Д.).

Конструктор без аргументов необходим для stl :: map, потому что он используется, когда operator [] вызывается с ключом, который еще не был сохранен картой. В этом случае operator [] вставляет новую запись, состоящую из нового ключа и значения, созданного с помощью конструктора без параметров. Затем возвращается это новое значение.

oo_olo_oo
источник
-2

Проверить, если:

  • Вы забыли ';' после объявления класса.
  • MyType должен был быть объявлен соответственно.
  • Конструктора по умолчанию там нет ...

Я думаю, что объявление std :: map кажется правильным.

Эрнан
источник
Компилируется нормально, если я добавляю конструктор по умолчанию.
Ник Болтон
-2

Скорее всего, потому, что этого требует std :: pair. std :: pair содержит два значения с использованием семантики значений, поэтому вам нужно иметь возможность создавать их экземпляры без параметров. Таким образом, код использует std :: pair в различных местах для возврата значений карты вызывающей стороне, и это обычно делается путем создания пустой пары и присвоения ей значений перед возвратом локальной пары.

Вы можете обойти это с помощью интеллектуальных указателей, используя map <int, smartptr <MyClass>>, но это добавляет накладные расходы на проверку нулевых указателей.

Jmucchiello
источник
2
+0. pair <T, U> может отлично использоваться с типами T и U, не имеющими конструкторов по умолчанию - единственное, что нельзя использовать в этом случае, - это собственный конструктор по умолчанию pair <T, U>. Никакая качественная реализация map <K, V> не будет использовать этот конструктор по умолчанию, потому что он ограничивает то, что могут быть K и V.
j_random_hacker