




Использование C++ 11:
#include <map>
using namespace std;
map<int, char> m = {{1, 'a'}, {3, 'b'}, {5, 'c'}, {7, 'd'}};
Используя Boost.Assign:
#include <map>
#include "boost/assign.hpp"
using namespace std;
using namespace boost::assign;
map<int, char> m = map_list_of (1, 'a') (3, 'b') (5, 'c') (7, 'd');
Он реализован «в основном» как перегруженный оператор, но если у вас когда-нибудь возникнет синтаксическая ошибка, наслаждайтесь этой строкой кода. Он втягивает 15 различных файлов .hpp для того, что займет у вас минуту или две. Код для развлечения? Используй это; в противном случае учитывайте стоимость по времени / размеру.
Boost отличный. Замечательные вещи там. Проблема в моем случае: мы не можем использовать его в качестве руководства компании. Не достаточно стандартно, недостаточно просто для среднего разработчика (не моя формулировка).
Прелесть ужасного кода шаблона, реализующего эти утилиты, заключается в том, что он аккуратно инкапсулирован в библиотеку, и конечному пользователю редко приходится сталкиваться со сложностью.
@QBziZ: Если ваша компания отказывается от использования Boost на том основании, что он не является «достаточно стандартным», мне интересно, какая библиотека C++ бы будет «достаточно стандартной». Boost - это стандартный компаньон то для кодировщика C++.
@DevSolar Я хотел бы использовать Boost больше, но его синергия с C++ 11 ужасна, и есть некоторые довольно ... любопытный дизайнерские решения (Iostreams требует, чтобы устройства можно было копировать, но делает потоки некопируемыми ... о, и также неподвижный - по крайней мере, в той реализации, которую я использую, может быть, они это исправили?).
Моя проблема с Boost (здесь и где-либо еще) заключается в том, что вы часто можете обойтись без него (в данном случае с C++ 11 или до C++ 11 с функцией). Boost добавляет значительные накладные расходы на время компиляции, имеет множество файлов для хранения в вашем репозитории (и вам приходится копировать / zip / extract, если вы создаете архив). Вот почему я стараюсь не использовать его. Я знаю, что вы можете выбрать, какие файлы включать / не включать, но обычно вам не нужно беспокоиться о перекрестных зависимостях Boost с самим собой, поэтому вы просто копируете все вокруг.
Моя проблема с Boost заключается в том, что у него часто есть несколько новых зависимостей библиотеки, что обычно означает БОЛЬШЕ пакетов, которые необходимо установить для правильной работы. Нам уже нужен libstdC++. Например, для библиотеки Boost ASIO требуется как минимум 2 новые библиотеки (возможно, больше), которые необходимо установить. C++ 11/14 значительно упрощает отказ от Boost.
@nobar очевидно, дублирующиеся ключи игнорируются, включается только первый экземпляр. Скорее всего, у меня просто нет нужных параметров компилятора для генерации предупреждений об этом.
Почему это не удается скомпилировать на vs2017 со стандартным значением C++ 17
Я бы обернул карту внутри статического объекта и поместил код инициализации карты в конструктор этого объекта, чтобы вы были уверены, что карта создается до выполнения кода инициализации.
Я с тобой в этом. Это также немного быстрее :)
Чуть быстрее, чем что? Глобальная статика с инициализатором? Нет, это не так (вспомните про RVO).
Хороший ответ. Буду рад, если увижу реальный пример кода
Лучший способ - использовать функцию:
#include <map>
using namespace std;
map<int,int> create_map()
{
map<int,int> m;
m[1] = 2;
m[3] = 4;
m[5] = 6;
return m;
}
map<int,int> m = create_map();
Хорошо, мне пришлось просто исправить ошибку сборки в MSVS'12, и это действительно помогло; быстрее, чем здесь настраивать буст.
Почему это «лучший»? Почему, например, это лучше, чем ответ @Dreamer?
Я думаю, что это «лучший», потому что он действительно прост и не зависит от других существующих структур (таких как Boost :: Assign или его повторная реализация). И по сравнению с ответом @Dreamer, я избегаю создания всей структуры только для инициализации карты ...
Есть ли для этой идиомы имя? В какое «время» работает эта функция create_map? Он работает до main, что такое имя для этой среды выполнения?
extern variables will not have their correct values in this "before main run-time constructor" если компилятор видел только объявление extern, но еще не столкнулся с фактическим определением переменной.
Нет, опасность состоит в том, что ничего не сказано, в каком порядке должны быть инициализированы статические переменные (по крайней мере, между единицами компиляции). Но это не проблема, связанная с этим вопросом. Это общая проблема со статическими переменными.
Как долго это проживет? Как распределяется его память? Немного смущен, поскольку я всегда использовал способ @eduffy.
Мне нравится этот ответ, потому что он прост и удобен для компилятора. Даже в C++ 11 map <int, int> m = {{1,1}, {2,2}}; не будет работать, если ваш компилятор еще не поддерживает его.
без повышения И без C++ 11 => +1. Обратите внимание, что функция может использоваться для инициализации const map<int,int> m = create_map() (и, следовательно, для инициализации константных членов класса в списке инициализации: struct MyClass {const map<int, int> m; MyClass(); }; MyClass::MyClass() : m(create_map())
Для меня это выглядит ужасно, но он работает в C++ 98, и это то, что я искал.
Сделать что-то похожее на буст - несложная задача. Вот класс всего с тремя функциями, включая конструктор, для воспроизведения того, что сделал boost (почти).
template <typename T, typename U>
class create_map
{
private:
std::map<T, U> m_map;
public:
create_map(const T& key, const U& val)
{
m_map[key] = val;
}
create_map<T, U>& operator()(const T& key, const U& val)
{
m_map[key] = val;
return *this;
}
operator std::map<T, U>()
{
return m_map;
}
};
Использование:
std::map mymap = create_map<int, int >(1,2)(3,4)(5,6);
Приведенный выше код лучше всего подходит для инициализации глобальных переменных или статических членов класса, который необходимо инициализировать, и вы не знаете, когда он будет использоваться первым, но вы хотите убедиться, что значения доступны в нем.
Если, скажем, вам нужно вставить элементы в существующий std :: map ... вот вам еще один класс.
template <typename MapType>
class map_add_values {
private:
MapType mMap;
public:
typedef typename MapType::key_type KeyType;
typedef typename MapType::mapped_type MappedType;
map_add_values(const KeyType& key, const MappedType& val)
{
mMap[key] = val;
}
map_add_values& operator()(const KeyType& key, const MappedType& val) {
mMap[key] = val;
return *this;
}
void to (MapType& map) {
map.insert(mMap.begin(), mMap.end());
}
};
Использование:
typedef std::map<int, int> Int2IntMap;
Int2IntMap testMap;
map_add_values<Int2IntMap>(1,2)(3,4)(5,6).to(testMap);
Посмотрите его в действии с GCC 4.7.2 здесь: http://ideone.com/3uYJiH
############### ВСЕ НИЖЕ ЭТО УСТАРЕЛО #################
РЕДАКТИРОВАТЬ: приведенный ниже класс map_add_values, который был оригинальным решением, которое я предложил, потерпит неудачу, когда дело доходит до GCC 4.5+. Пожалуйста, посмотрите на приведенный выше код, чтобы узнать, как добавить значения добавлять к существующей карте.
template<typename T, typename U>
class map_add_values
{
private:
std::map<T,U>& m_map;
public:
map_add_values(std::map<T, U>& _map):m_map(_map){}
map_add_values& operator()(const T& _key, const U& _val)
{
m_map[key] = val;
return *this;
}
};Использование:
std::map<int, int> my_map; // Later somewhere along the code map_add_values<int,int>(my_map)(1,2)(3,4)(5,6);
ПРИМЕЧАНИЕ. Ранее я использовал operator [] для добавления фактических значений. Это невозможно, как прокомментировал dalle.
#################### КОНЕЦ УСТАРЕВШЕГО СЕКЦИИ ########################################
Я использую ваш первый образец как <int, string> для привязки номеров ошибок (из перечисления) к сообщениям - он работает как шарм - спасибо.
operator[] принимает только один аргумент.
@dalle: Хороший улов! По какой-то причине я думал, что перегруженные операторы [] могут принимать больше.
Это фантастический ответ. Жаль, что ОП не выбрал ни одного. Вы заслуживаете мега-реквизита.
map_add_values не работает в gcc, который жалуется: error: conflicting declaration ‘map_add_values<int, int> my_map’error: ‘my_map’ has a previous declaration as ‘std::map<int, int> my_map’
@martinwang Какая версия gcc? Читая ошибку компилятора, кажется, что вы сделали что-то вроде map_add_values my_map.... Вы используете его, как показано в моем примере использования?
GCC4.5.1. Твой пример я пробовал. GCC просто сбивает с толку зависание анонимного объекта. class A{}; A a(); в порядке. class A{}; A(); приводит к ошибке компиляции.
@MartinWang, понятно. Это тот случай, когда GCC считает, что утверждение map_add_values<int,int>(my_map)(1,2)(3,4); похоже на map_add_values<int, int> my_map(1,2)(3,4);. Я ищу элегантное решение.
@MartinWang, у меня есть более новое решение. Кажется, он работает с GCC 4.7.2, и вы можете видеть, что он компилируется на ideone. Я переместил свое исходное (сломанное) решение в конец для исторического отслеживания.
Ну, ваш пример используется в main (). Подвешивание анонимного объекта по-прежнему запрещено в глобальном пространстве имен. Нравится class A {public: A(int){} void to(){}}; A(1).to();
Мое решение использует уникальное пространство имен, в котором анонимный объект присваивается переменной. Для простоты весь код, определенный в макросе, используется как MAP_ADD(mymap,int,int, (1,2)(3,4));.
@MartinWang - Можно ли передать указатель на структуру вместо значения int? map <int, указатель на класс> вместо map <int, int> в этом примере? Благодарность
@ejuser вы можете передать тип указателя, в чем ваш смысл?
@MartinWang - На самом деле есть 2 вопроса, связанных с этим (при необходимости я могу открыть другой поток) <br/> Вопрос 1: (отдельная проблема) - Невозможно использовать строку <br/> std :: map mymap = create_map <int, интервал> (1,2) (3,4) (5,6); <br/> Вопрос 2: Будут ли утечки памяти, если не очистить карту? Нужно ли нам добавлять какие-либо конкретные явные команды для очистки карты (бесплатно (карта))? <br/> Вопрос 3. Есть ли способ расширить это как unique_pointer до map <int, <unique_ptr> class> <br/>
@ejuser 1. покажите свой код и проблемы. 2. Нет необходимости в delete/free, если не используется new/malloc. 3. Аргументы шаблона - это типы, а не конкретный указатель, я вас не понимаю.
Позвольте нам продолжить обсуждение в чате.
Привет, хорошее решение, но я думаю, что map_add_values было бы лучше таким образом: template <typename MapType> class map_adder {private: MapType & mMap; общедоступные: typedef typename MapType :: key_type KeyType; typedef typename MapType :: mapped_type MappedType; map_adder (MapType & Map): mMap (Карта) {} map_adder & operator () (const KeyType & key, const MappedType & val) {mMap [key] = val; return * this; }}; шаблон <typename Maptype> map_adder <Maptype> map_add_values (Maptype & data2change) {return map_adder <Maptype> (data2change); } -> map_add_values (mymap) (3,3);
Это похоже на PierreBdR, без копирования карты.
#include <map>
using namespace std;
bool create_map(map<int,int> &m)
{
m[1] = 2;
m[3] = 4;
m[5] = 6;
return true;
}
static map<int,int> m;
static bool _dummy = create_map (m);
Вероятно, его все равно бы не скопировали.
но в этом случае карта не может быть статической константой, не так ли?
Вот еще один способ использования двухэлементного конструктора данных. Для его инициализации не требуется никаких функций. Нет стороннего кода (Boost), нет статических функций или объектов, нет трюков, просто C++:
#include <map>
#include <string>
typedef std::map<std::string, int> MyMap;
const MyMap::value_type rawData[] = {
MyMap::value_type("hello", 42),
MyMap::value_type("world", 88),
};
const int numElems = sizeof rawData / sizeof rawData[0];
MyMap myMap(rawData, rawData + numElems);
Поскольку я написал этот ответ, C++ 11 отсутствует. Теперь вы можете напрямую инициализировать контейнеры STL, используя новую функцию списка инициализаторов:
const MyMap myMap = { {"hello", 42}, {"world", 88} };
У вас есть несколько очень хороших ответов, но для меня это похоже на случай, когда все, что вы знаете, - это молоток ...
Самый простой ответ на вопрос, почему нет стандартного способа инициализировать статическую карту, заключается в том, что нет веской причины когда-либо использовать статическую карту ...
Карта - это структура, предназначенная для быстрого поиска неизвестного набора элементов. Если вы знаете элементы заранее, просто используйте C-массив. Введите значения в отсортированном порядке или выполните сортировку по ним, если вы не можете этого сделать. Затем вы можете получить производительность log (n), используя stl :: functions для циклических записей, lower_bound / upper_bound. Когда я тестировал это ранее, они обычно работают как минимум в 4 раза быстрее, чем карта.
Преимущества многочисленны ... - более высокая производительность (* 4, я измерял на многих типах процессоров, всегда около 4) - более простая отладка. Просто с линейной компоновкой проще увидеть, что происходит. - Тривиальные реализации операций копирования, если в этом возникнет необходимость. - Он не выделяет память во время выполнения, поэтому никогда не вызовет исключение. - Это стандартный интерфейс, поэтому его очень легко использовать, используя библиотеки DLL, языки и т. д.
Я мог бы продолжить, но если вы хотите большего, почему бы не заглянуть в многочисленные блоги Страуструпа по этой теме.
Производительность - не единственная причина использования карты. Например, есть много случаев, когда вы хотите связать значения вместе (например, код ошибки с сообщением об ошибке), а карта делает использование и доступ относительно простым. Но ссылка на эти записи в блоге может быть интересной, может я что-то делаю не так.
Массив намного проще и имеет более высокую производительность, если вы можете его использовать. Но если индексы (ключи) не являются смежными и широко разнесены, вам понадобится карта.
map также является полезной формой для представления частичной функции (функции в математическом смысле; но также в некотором роде в смысле программирования). Массив этого не делает. Вы не можете, скажем, искать данные из массива, используя строку.
Ваш ответ не является попыткой ответить на действительный вопрос, а вместо этого размышляет об ограничениях языка, предлагает решения различных проблем, следовательно, голосует против. Реальный сценарий - отображение кодов ошибок библиотеки (непрерывных или нет) в текстовые строки. С массивом время поиска составляет O (n), что можно улучшить статическим сопоставлением с O (log (n)).
Если действительно «нет веской причины когда-либо использовать статическую карту ...», то очень странно, что синтаксис (списки инициализаторов), упрощающий их использование, был добавлен в C++ 11.
@Tosha: Я думаю, что ответ относится к вашему сценарию и описывает, как его можно искать в O (log (n)) без использования карты. См. Где я могу найти «полезный» алгоритм двоичного поиска C++?.
Просто хотел поделиться чистой работой С ++ 98:
#include <map>
std::map<std::string, std::string> aka;
struct akaInit
{
akaInit()
{
aka[ "George" ] = "John";
aka[ "Joe" ] = "Al";
aka[ "Phil" ] = "Sue";
aka[ "Smitty" ] = "Yando";
}
} AkaInit;
это не работает для объекта без конструктора по умолчанию, метод вставки должен быть предпочтительным IMHO
Можешь попробовать:
std::map <int, int> mymap =
{
std::pair <int, int> (1, 1),
std::pair <int, int> (2, 2),
std::pair <int, int> (2, 2)
};
Вы не можете использовать списки инициализаторов с неагрегатными типами до C++ 11, и в этом случае вы также можете использовать более короткий синтаксис {1, 2} вместо std::pair<int, int>(1, 2).
Например:
const std::map<LogLevel, const char*> g_log_levels_dsc =
{
{ LogLevel::Disabled, "[---]" },
{ LogLevel::Info, "[inf]" },
{ LogLevel::Warning, "[wrn]" },
{ LogLevel::Error, "[err]" },
{ LogLevel::Debug, "[dbg]" }
};
Если карта является членом данных класса, вы можете инициализировать ее непосредственно в заголовке следующим образом (начиная с C++ 17):
// Example
template<>
class StringConverter<CacheMode> final
{
public:
static auto convert(CacheMode mode) -> const std::string&
{
// validate...
return s_modes.at(mode);
}
private:
static inline const std::map<CacheMode, std::string> s_modes =
{
{ CacheMode::All, "All" },
{ CacheMode::Selective, "Selective" },
{ CacheMode::None, "None" }
// etc
};
};
Если вы застряли на C++ 98 и не хотите использовать ускорение, вот решение, которое я использую, когда мне нужно инициализировать статическую карту:
typedef std::pair< int, char > elemPair_t;
elemPair_t elemPairs[] =
{
elemPair_t( 1, 'a'),
elemPair_t( 3, 'b' ),
elemPair_t( 5, 'c' ),
elemPair_t( 7, 'd' )
};
const std::map< int, char > myMap( &elemPairs[ 0 ], &elemPairs[ sizeof( elemPairs ) / sizeof( elemPairs[ 0 ] ) ] );
Каждый раз, когда я вижу что-то подобное, сделанное с помощью C++, я думаю обо всем ужасном коде шаблона, который, должно быть, стоит за этим. Хороший пример!