Đang khởi tạo tĩnh std :: map <int, int> trong C ++


447

Cách đúng để khởi tạo một bản đồ tĩnh là gì? Chúng ta có cần một hàm tĩnh sẽ khởi tạo nó không?

Câu trả lời:


619

Sử dụng C ++ 11:

#include <map>
using namespace std;

map<int, char> m = {{1, 'a'}, {3, 'b'}, {5, 'c'}, {7, 'd'}};

Sử dụng 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');

115
Mỗi lần tôi nhìn thấy một cái gì đó giống như được thực hiện với C ++, tôi nghĩ về tất cả các mã mẫu khủng khiếp phải ở đằng sau nó. Ví dụ tốt!
Greg Hewgill

34
Vẻ đẹp của tất cả các mã mẫu khủng khiếp thực hiện các tiện ích này là nó được gói gọn trong một thư viện và người dùng cuối hiếm khi cần phải giải quyết sự phức tạp.
Steve Guidi

45
@QBziZ: Nếu công ty của bạn từ chối sử dụng Boost với lý do nó không đủ "tiêu chuẩn", tôi tự hỏi thư viện C ++ nào sẽ "đủ tiêu chuẩn". Boost là những bạn đồng hành tiêu chuẩn cho C ++ coder.
DevSolar

47
Vấn đề của tôi với Boost (ở đây và ở nơi khác) là bạn có thể thường xuyên sử dụng mà không có nó (trong trường hợp này là với C ++ 11 hoặc trước C ++ 11 với một chức năng ). Boost thêm một khoảng thời gian biên dịch đáng kể, có hàng tấn tệp để lưu trữ vào kho lưu trữ của bạn (và phải sao chép xung quanh / zip / giải nén nếu bạn đang lưu trữ). Đó là lý do tôi cố gắng không sử dụng nó. Tôi biết bạn có thể chọn những tập tin bao gồm / không bao gồm, nhưng bạn thường không muốn phải lo lắng về sự phụ thuộc chéo của Boost với chính nó để bạn chỉ cần sao chép toàn bộ nội dung xung quanh.
bobobobo

7
Vấn đề của tôi với Boost là nó thường có một số phụ thuộc thư viện mới, thường có nghĩa là các gói THÊM cần được cài đặt để hoạt động chính xác. Chúng tôi đã cần libstdc ++. Ví dụ: thư viện Boost ASIO, yêu cầu ít nhất 2 thư viện mới (có thể nhiều hơn) cần được cài đặt. C ++ 11/14 không làm cho nó dễ dàng hơn nhiều khi không cần Boost.
Rahly

135

Cách tốt nhất là sử dụng một chức năng:

#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();

18
Tại sao điều này là 'tốt nhất'? Tại sao ví dụ tốt hơn câu trả lời của @ Dreamer?
Hầu tước Lorne

6
Tôi nghĩ đó là "tốt nhất" bởi vì nó thực sự đơn giản và không phụ thuộc vào các cấu trúc khác hiện có (chẳng hạn như Boost :: Assign hoặc triển khai lại nó). Và so với câu trả lời của @ Dreamer's, tôi tránh tạo toàn bộ cấu trúc chỉ để khởi tạo bản đồ ...
PierreBdR

3
Lưu ý có một mối nguy hiểm ở đây . externcác biến sẽ không có giá trị chính xác của chúng trong "trước hàm tạo thời gian chạy chính" này nếu trình biên dịch chỉ nhìn thấy externkhai báo, nhưng chưa chạy vào định nghĩa biến thực tế .
bobobobo

5
Không, điều nguy hiểm là không có gì để nói theo thứ tự các biến tĩnh nên được khởi tạo (ít nhất là trên các đơn vị biên dịch). Nhưng đây không phải là một vấn đề liên quan đến câu hỏi này. Đây là một vấn đề chung với các biến tĩnh.
PierreBdR

5
không tăng VÀ không C ++ 11 => +1. Lưu ý rằng hàm có thể được sử dụng để khởi tạo một const map<int,int> m = create_map()(và do đó, khởi tạo các thành viên const của một lớp trong danh sách khởi tạo:struct MyClass {const map<int, int> m; MyClass(); }; MyClass::MyClass() : m(create_map())
ribamar 13/03/2015

115

Đây không phải là một vấn đề phức tạp để làm một cái gì đó tương tự để tăng cường. Đây là một lớp chỉ có ba hàm, bao gồm hàm tạo, để sao chép những gì boost đã làm (gần như).

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;
    }
};

Sử dụng:

std :: map mymap = created_map <int, int> (1,2) (3,4) (5,6);

Đoạn mã trên hoạt động tốt nhất để khởi tạo các biến toàn cục hoặc các thành viên tĩnh của một lớp cần được khởi tạo và bạn không biết khi nào nó được sử dụng trước nhưng bạn muốn đảm bảo rằng các giá trị có sẵn trong nó.

Nếu nói, bạn phải chèn các phần tử vào std :: map ... hiện tại đây là một lớp khác dành cho bạn.

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());
    }
};

Sử dụng:

typedef std::map<int, int> Int2IntMap;
Int2IntMap testMap;
map_add_values<Int2IntMap>(1,2)(3,4)(5,6).to(testMap);

Xem nó hoạt động với GCC 4.7.2 tại đây: http://ideone.com/3uYJiH

############### MỌI THỨ DƯỚI ĐÂY LÀ OBSOLLEX #################

EDIT : map_add_valuesLớp bên dưới, là giải pháp ban đầu tôi đã đề xuất, sẽ thất bại khi nói đến GCC 4.5+. Vui lòng xem mã ở trên để biết cách thêm giá trị vào bản đồ hiện có.


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;
    }
};

Sử dụng:

std :: map <int, int> my_map;
// Sau đó ở đâu đó dọc theo mã
map_add_values ​​<int, int> (my_map) (1,2) (3,4) (5,6);

LƯU Ý: Trước đây tôi đã sử dụng một operator []để thêm các giá trị thực tế. Điều này là không thể như nhận xét của dalle.

##################### KẾT THÚC CỦA PHẦN OBSOLLEX ######################


3
Tôi đang sử dụng mẫu đầu tiên của bạn dưới dạng <int, chuỗi> để liên kết các số lỗi (từ enum) với các tin nhắn - nó hoạt động như một lá bùa - cảm ơn bạn.
slashmais

1
operator[]chỉ mất một đối số duy nhất.
dalle

1
@dalle: Bắt tốt! Vì một số lý do, tôi nghĩ rằng các nhà khai thác [] quá tải có thể chấp nhận nhiều hơn.
Vite Falcon

2
Đây là một câu trả lời tuyệt vời. Thật xấu hổ khi OP không bao giờ chọn một. Bạn xứng đáng với đạo cụ lớn.
Thomas Thorogood

map_add_values ​​không hoạt động trong gcc, điều này phàn nàn: error: conflicting declaration ‘map_add_values<int, int> my_map’ error: ‘my_map’ has a previous declaration as ‘std::map<int, int> my_map’
Martin Wang

42

Đây là một cách khác sử dụng hàm tạo dữ liệu 2 phần tử. Không có chức năng là cần thiết để khởi tạo nó. Không có mã bên thứ 3 (Boost), không có chức năng hoặc đối tượng tĩnh, không có thủ thuật, chỉ đơn giản là 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);

Vì tôi đã viết câu trả lời này nên C ++ 11 đã ra. Bây giờ bạn có thể trực tiếp khởi tạo các thùng chứa STL bằng tính năng danh sách khởi tạo mới:

const MyMap myMap = { {"hello", 42}, {"world", 88} };

25

Ví dụ:

const std::map<LogLevel, const char*> g_log_levels_dsc =
{
    { LogLevel::Disabled, "[---]" },
    { LogLevel::Info,     "[inf]" },
    { LogLevel::Warning,  "[wrn]" },
    { LogLevel::Error,    "[err]" },
    { LogLevel::Debug,    "[dbg]" }
};

Nếu bản đồ là thành viên dữ liệu của một lớp, bạn có thể khởi tạo nó trực tiếp trong tiêu đề theo cách sau (kể từ 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
        };
}; 

24

Tôi sẽ bọc bản đồ bên trong một đối tượng tĩnh và đặt mã khởi tạo bản đồ vào hàm tạo của đối tượng này, bằng cách này, bạn chắc chắn rằng bản đồ được tạo trước khi mã khởi tạo được thực thi.


1
Tôi với bạn về điều này. Nó cũng nhanh hơn một chút :)
QBziZ

2
Tad nhanh hơn cái gì? Một tĩnh toàn cầu với một bộ khởi tạo? Không, không phải (nhớ về RVO).
Pavel Minaev

7
Câu trả lời tốt đẹp. Tôi sẽ rất vui nếu tôi thấy mã ví dụ thực tế
Sungguk Lim

18

Chỉ muốn chia sẻ một công việc thuần túy C ++ 98 xung quanh:

#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;

2
điều này không hoạt động cho đối tượng mà không có hàm tạo mặc định, phương thức chèn nên được ưu tiên IMHO
Alessandro Teruzzi

16

Bạn co thể thử:

std::map <int, int> mymap = 
{
        std::pair <int, int> (1, 1),
        std::pair <int, int> (2, 2),
        std::pair <int, int> (2, 2)
};

1
Bạn không thể sử dụng danh sách trình khởi tạo với các loại không tổng hợp trước C ++ 11, trong trường hợp đó bạn cũng có thể sử dụng cú pháp ngắn hơn {1, 2}thay vì std::pair<int, int>(1, 2).
Ferruccio

9

Điều này tương tự PierreBdR, mà không cần sao chép bản đồ.

#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);

12
Nó có lẽ sẽ không được sao chép.
GManNickG

2
Nhưng cách này bản đồ không thể là tĩnh const, phải không?
xmoex

6

Nếu bạn bị mắc kẹt với C ++ 98 và không muốn sử dụng boost, đây là giải pháp tôi sử dụng khi tôi cần khởi tạo bản đồ tĩnh:

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 ] ) ] );

-4

Bạn có một số câu trả lời rất hay ở đây, nhưng với tôi, nó giống như một trường hợp "khi tất cả những gì bạn biết là một cái búa" ...

Câu trả lời đơn giản nhất về lý do tại sao không có cách chuẩn để khởi tạo bản đồ tĩnh, là không có lý do chính đáng để sử dụng bản đồ tĩnh ...

Bản đồ là một cấu trúc được thiết kế để tra cứu nhanh, gồm một tập hợp các yếu tố không xác định. Nếu bạn biết các yếu tố trước khi sử dụng, chỉ cần sử dụng một mảng C. Nhập các giá trị theo cách được sắp xếp hoặc chạy sắp xếp trên chúng, nếu bạn không thể làm điều này. Sau đó, bạn có thể nhận được hiệu suất log (n) bằng cách sử dụng các hàm stl :: để lặp lại các mục, low_bound / Upper_bound. Khi tôi đã thử nghiệm điều này trước đây, họ thường thực hiện nhanh hơn ít nhất 4 lần so với bản đồ.

Ưu điểm là gấp nhiều lần ... - hiệu suất nhanh hơn (* 4, tôi đã đo trên nhiều loại CPU, nó luôn ở mức 4) - gỡ lỗi đơn giản hơn. Nó chỉ dễ dàng hơn để xem những gì đang xảy ra với một bố cục tuyến tính. - Việc triển khai tầm thường của các hoạt động sao chép, nếu điều đó trở nên cần thiết. - Nó phân bổ không có bộ nhớ trong thời gian chạy, vì vậy sẽ không bao giờ ném ngoại lệ. - Đó là một giao diện chuẩn và rất dễ chia sẻ, DLL hoặc ngôn ngữ, v.v.

Tôi có thể tiếp tục, nhưng nếu bạn muốn nhiều hơn, tại sao không xem nhiều blog của Stroustrup về chủ đề này.


8
Hiệu suất không phải là lý do duy nhất để sử dụng bản đồ. Ví dụ: có nhiều trường hợp, trong đó bạn muốn liên kết các giá trị với nhau (ví dụ: mã lỗi với thông báo lỗi) và bản đồ làm cho việc sử dụng và truy cập tương đối đơn giản. Nhưng một liên kết đến các mục blog này có thể thú vị, có thể tôi đang làm gì đó sai.
MatthiasB

5
Một mảng dễ dàng hơn nhiều và có hiệu suất cao hơn nếu bạn có thể sử dụng nó. Nhưng nếu các chỉ số (khóa) không liền kề và cách nhau rộng rãi, bạn cần một bản đồ.
KarlU

1
A mapcũng là một hình thức hữu ích để biểu diễn một hàm một phần (hàm theo nghĩa toán học; nhưng cũng có thể, theo nghĩa lập trình). Một mảng không làm điều đó. Bạn không thể, giả sử, tra cứu dữ liệu từ một mảng bằng chuỗi.
einpoklum

3
Câu trả lời của bạn không cố gắng trả lời câu hỏi hợp lệ, và thay vào đó suy đoán về những hạn chế của ngôn ngữ, đề xuất giải pháp cho các vấn đề khác nhau, do đó downvote. Một kịch bản thực tế - ánh xạ (liên tục hoặc không) mã lỗi thư viện thành chuỗi văn bản. Với mảng, thời gian tìm kiếm là O (n), có thể được cải thiện bằng cách ánh xạ tĩnh tới O (log (n)).
Tosha

2
Nếu thực sự "không có lý do chính đáng nào để sử dụng bản đồ tĩnh ..." thì thật lạ khi cú pháp (danh sách khởi tạo) giúp chúng dễ sử dụng đã được thêm vào trong C ++ 11.
ellvdben
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.