Чому аргумент типу мапи C ++ вимагає порожнього конструктора при використанні []?


98

Див. Також стандартний перелік С ++ та типи, що створюються за замовчуванням

Не головна проблема, просто дратує, оскільки я не хочу, щоб мій клас ніколи не створювався без конкретних аргументів.

#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, за умови, що вказано typedef для MyType та крапку з комою додано до кінця класу. Ви повинні робити щось інше (наприклад, зателефонувати оператору [], як згадує bb) - будь ласка, опублікуйте повний код.
j_random_hacker

Ах, так, ти маєш рацію. Зроблю.
Нік Болтон,

Так, без використання myMap ви не знаєте, що потрібно скомпілювати для класу map. Який постачальник бібліотеки stl і версія також також можуть допомогти.
Greg Domjan,

Відповіді:


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

Умова "Карта ще не містить такого об'єкта" буде оцінена під час виконання. Чому помилка часу компіляції?
Гаурав Сінгх,

8

Так. Значення в контейнерах 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 міг би займатися чимось стосовно оператора []?
Нік Болтон,

12
Цей код, ймовірно, компілюється, тому що ви не викликаєте myMap []
jfritz42

3

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

Конструктор без аргументів потрібен stl :: map, оскільки він використовується, коли оператор [] викликається за допомогою ключа, який ще не зберігався на карті. У цьому випадку оператор [] вставляє новий запис, що складається з нового ключа і значення, побудованого за допомогою безпараметричного конструктора. І це нове значення потім повертається.


-2

Перевірте, чи:

  • Ви забули ';' після оголошення класу.
  • MyType слід було оголосити відповідним чином.
  • Там немає конструктора за замовчуванням ...

Я думаю, декларація std :: map здається правильною.


Компілюється добре, якщо я додаю конструктор за замовчуванням.
Нік Болтон,

-2

Швидше за все тому, що цього вимагає std :: pair. std :: pair вміщує два значення за допомогою семантики значень, тому вам потрібно мати можливість створити їх екземпляр без параметрів. Отже, код використовує std :: pair в різних місцях для повернення значень карти викликає, і це зазвичай робиться шляхом створення порожньої пари та присвоєння значень їй перед поверненням локальної пари.

Ви могли б обійти це за допомогою розумних покажчиків, використовуючи карту <int, smartptr <MyClass>>, але це додає накладні витрати на перевірку нульових покажчиків.


2
+0. пара <T, U> може бути добре використана для типів T та U, у яких відсутні конструктори за замовчуванням - єдине, що не можна використовувати в цьому випадку, це власний конструктор пари <T, U>. Жодна гідна реалізація map <K, V> не використовувала б цей конструктор за замовчуванням, оскільки він обмежує те, якими можуть бути K і V.
j_random_hacker
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.