Làm thế nào để khởi tạo một bản đồ const tĩnh riêng trong C ++?


108

Tôi chỉ cần từ điển hoặc mảng kết hợp string=> int.

Có loại bản đồ C ++ cho trường hợp này.

Nhưng tôi chỉ cần một cá thể forall bản đồ (-> static) và không thể thay đổi bản đồ này (-> const);

Tôi đã tìm thấy cách này với thư viện tăng cường

 std::map<int, char> example = 
      boost::assign::map_list_of(1, 'a') (2, 'b') (3, 'c');

Có giải pháp nào khác mà không có lib này? Tôi đã thử một cái gì đó như thế này, nhưng luôn có một số vấn đề với việc khởi tạo bản đồ.

class myClass{
private:
    static map<int,int> create_map()
        {
          map<int,int> m;
          m[1] = 2;
          m[3] = 4;
          m[5] = 6;
          return m;
        }
    static map<int,int> myMap =  create_map();

};

1
Những vấn đề bạn tham khảo là gì? Bạn đang cố gắng sử dụng bản đồ này từ một biến / hằng số tĩnh toàn cục khác?
Péter Török

Đó không phải là một chuỗi mảng kết hợp => int, bạn đang ánh xạ một int với một char. v = k + 'a' - 1.
Johnsyweb

Câu trả lời:


107
#include <map>
using namespace std;

struct A{
    static map<int,int> create_map()
        {
          map<int,int> m;
          m[1] = 2;
          m[3] = 4;
          m[5] = 6;
          return m;
        }
    static const map<int,int> myMap;

};

const map<int,int> A:: myMap =  A::create_map();

int main() {
}

3
1 vì đơn giản, tất nhiên bằng cách sử dụng Boost.Assignnhư thiết kế khá gọn gàng quá :)
Matthieu M.

5
+1, cảm ơn. Lưu ý: Tôi phải đặt dòng khởi tạo trong tệp triển khai của mình; để nó trong tệp tiêu đề đã gây cho tôi lỗi do nhiều định nghĩa (mã khởi tạo sẽ chạy bất cứ khi nào tiêu đề được bao gồm ở đâu đó).
System.Cats.Lol

1
Với g ++ v4.7.3, điều này sẽ biên dịch, cho đến khi tôi thêm cout << A::myMap[1];vào main(). Nó đưa ra một lỗi. Lỗi không xảy ra nếu tôi xóa các constvòng loại, vì vậy tôi đoán bản đồ operator[]không thể xử lý a const map, ít nhất, không phải trong việc triển khai g ++ của thư viện C ++.
Craig McQueen,

2
Lỗi là:const_map.cpp:22:23: error: passing ‘const std::map<int, int>’ as ‘this’ argument of ‘std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const key_type&) [with _Key = int; _Tp = int; _Compare = std::less<int>; _Alloc = std::allocator<std::pair<const int, int> >; std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = int; std::map<_Key, _Tp, _Compare, _Alloc>::key_type = int]’ discards qualifiers [-fpermissive]
Craig McQueen,

4
Thật vậy, toán tử của bản đồ [] không thể hoạt động trên bản đồ const vì toán tử đó tạo mục nhập được tham chiếu nếu nó không tồn tại (vì nó trả về một tham chiếu đến giá trị được ánh xạ). C ++ 11 đã giới thiệu phương thức at (KeyValT key) cho phép bạn truy cập mục bằng một khóa nhất định, đưa ra một ngoại lệ nếu nó không tồn tại. ( en.cppreference.com/w/cpp/container/map/at ) Phương thức này sẽ hoạt động trên các thể hiện const nhưng không thể được sử dụng để chèn một phần tử trên một thể hiện không phải const (cũng như toán tử []).
mbargiel

108

Tiêu chuẩn C ++ 11 đã giới thiệu cách khởi tạo thống nhất giúp việc này trở nên đơn giản hơn nhiều nếu trình biên dịch của bạn hỗ trợ nó:

//myClass.hpp
class myClass {
  private:
    static map<int,int> myMap;
};


//myClass.cpp
map<int,int> myClass::myMap = {
   {1, 2},
   {3, 4},
   {5, 6}
};

Xem thêm phần này từ Professional C ++ , trên bản đồ không có thứ tự.


Chúng ta có cần dấu bằng trong tệp cpp không?
phoad

@phoad: Dấu bằng là thừa.
Jinxed

Cảm ơn bạn đã hiển thị cách sử dụng. Nó thực sự hữu ích khi hiểu cách sửa đổi các biến tĩnh.
Người dùng9102d82 Ngày

12

Tôi đã làm nó! :)

Hoạt động tốt mà không cần C ++ 11

class MyClass {
    typedef std::map<std::string, int> MyMap;

    struct T {
        const char* Name;
        int Num;

        operator MyMap::value_type() const {
            return std::pair<std::string, int>(Name, Num);
        }
    };

    static const T MapPairs[];
    static const MyMap TheMap;
};

const MyClass::T MyClass::MapPairs[] = {
    { "Jan", 1 }, { "Feb", 2 }, { "Mar", 3 }
};

const MyClass::MyMap MyClass::TheMap(MapPairs, MapPairs + 3);

11

Nếu bạn thấy boost::assign::map_list_ofhữu ích, nhưng không thể sử dụng vì lý do nào đó, bạn có thể viết bài của riêng mình :

template<class K, class V>
struct map_list_of_type {
  typedef std::map<K, V> Map;
  Map data;
  map_list_of_type(K k, V v) { data[k] = v; }
  map_list_of_type& operator()(K k, V v) { data[k] = v; return *this; }
  operator Map const&() const { return data; }
};
template<class K, class V>
map_list_of_type<K, V> my_map_list_of(K k, V v) {
  return map_list_of_type<K, V>(k, v);
}

int main() {
  std::map<int, char> example = 
    my_map_list_of(1, 'a') (2, 'b') (3, 'c');
  cout << example << '\n';
}

Thật hữu ích khi biết những thứ như vậy hoạt động như thế nào, đặc biệt là khi chúng quá ngắn, nhưng trong trường hợp này, tôi sẽ sử dụng một hàm:

a.hpp

struct A {
  static map<int, int> const m;
};

a.cpp

namespace {
map<int,int> create_map() {
  map<int, int> m;
  m[1] = 2; // etc.
  return m;
}
}

map<int, int> const A::m = create_map();

6

Một cách tiếp cận vấn đề khác:

struct A {
    static const map<int, string> * singleton_map() {
        static map<int, string>* m = NULL;
        if (!m) {
            m = new map<int, string>;
            m[42] = "42"
            // ... other initializations
        }
        return m;
    }

    // rest of the class
}

Điều này hiệu quả hơn, vì không có bản sao một kiểu từ ngăn xếp sang đống (bao gồm hàm tạo, hàm hủy trên tất cả các phần tử). Điều này có quan trọng hay không phụ thuộc vào trường hợp sử dụng của bạn. Không quan trọng với chuỗi! (nhưng bạn có thể thấy hoặc không thấy phiên bản này "sạch" hơn)


3
RVO loại bỏ sự sao chép trong câu trả lời của tôi và Neil.

6

Nếu bản đồ chỉ chứa các mục nhập đã biết tại thời điểm biên dịch và các khóa của bản đồ là số nguyên, thì bạn hoàn toàn không cần sử dụng bản đồ.

char get_value(int key)
{
    switch (key)
    {
        case 1:
            return 'a';
        case 2:
            return 'b';
        case 3:
            return 'c';
        default:
            // Do whatever is appropriate when the key is not valid
    }
}

5
+1 vì chỉ ra rằng bản đồ là không cần thiết, tuy nhiên, bạn không thể lặp lại điều này
Viktor Sehr

4
switchTuy nhiên, điều đó thật tồi tệ. Tại sao không return key + 'a' - 1?
Johnsyweb

12
@Johnsyweb. Tôi giả định rằng bản đồ do người đăng ban đầu cung cấp chỉ được trình bày như một ví dụ và không chỉ ra bản đồ thực tế mà anh ta có. Do đó, tôi cũng giả định rằng điều đó return key + 'a' - 1sẽ không hiệu quả với việc lập bản đồ thực tế của anh ấy.
Matthew T. Staebler

3

Bạn có thể thử điều này:

MyClass.h

class MyClass {
private:
    static const std::map<key, value> m_myMap; 
    static const std::map<key, value> createMyStaticConstantMap();
public:
    static std::map<key, value> getMyConstantStaticMap( return m_myMap );
}; //MyClass

MyClass.cpp

#include "MyClass.h"

const std::map<key, value> MyClass::m_myMap = MyClass::createMyStaticConstantMap();

const std::map<key, value> MyClass::createMyStaticConstantMap() {
    std::map<key, value> mMap;
    mMap.insert( std::make_pair( key1, value1 ) );
    mMap.insert( std::make_pair( key2, value2 ) );
    // ....
    mMap.insert( std::make_pair( lastKey, lastValue ) ); 
    return mMap;
} // createMyStaticConstantMap

Với việc triển khai này, bản đồ tĩnh hằng số các lớp của bạn là một thành viên riêng và có thể được truy cập vào các lớp khác bằng phương thức public get. Nếu không, vì nó không đổi và không thể thay đổi, bạn có thể loại bỏ phương thức public get và chuyển biến bản đồ vào phần public của các lớp. Tuy nhiên, tôi sẽ để phương thức createMap ở chế độ riêng tư hoặc được bảo vệ nếu yêu cầu kế thừa và hoặc đa hình. Dưới đây là một số mẫu sử dụng.

 std::map<key,value> m1 = MyClass::getMyMap();
 // then do work on m1 or
 unsigned index = some predetermined value
 MyClass::getMyMap().at( index ); // As long as index is valid this will 
 // retun map.second or map->second value so if in this case key is an
 // unsigned and value is a std::string then you could do
 std::cout << std::string( MyClass::getMyMap().at( some index that exists in map ) ); 
// and it will print out to the console the string locted in the map at this index. 
//You can do this before any class object is instantiated or declared. 

 //If you are using a pointer to your class such as:
 std::shared_ptr<MyClass> || std::unique_ptr<MyClass>
 // Then it would look like this:
 pMyClass->getMyMap().at( index ); // And Will do the same as above
 // Even if you have not yet called the std pointer's reset method on
 // this class object. 

 // This will only work on static methods only, and all data in static methods must be available first.

Tôi đã chỉnh sửa bài đăng ban đầu của mình, không có gì sai với mã gốc mà tôi đã đăng vì nó được biên dịch, xây dựng và chạy chính xác, chỉ là phiên bản đầu tiên của tôi mà tôi trình bày dưới dạng câu trả lời, bản đồ được khai báo là công khai và bản đồ const nhưng không tĩnh.


2

Nếu bạn đang sử dụng một trình biên dịch vẫn không hỗ trợ khởi tạo phổ quát hoặc bạn đã đặt trước khi sử dụng Boost, một giải pháp thay thế khả thi khác sẽ như sau

std::map<int, int> m = [] () {
    std::pair<int,int> _m[] = {
        std::make_pair(1 , sizeof(2)),
        std::make_pair(3 , sizeof(4)),
        std::make_pair(5 , sizeof(6))};
    std::map<int, int> m;
    for (auto data: _m)
    {
        m[data.first] = data.second;
    }
    return m;
}();

0

Một lời gọi hàm không thể xuất hiện trong một biểu thức hằng.

hãy thử cái này: (chỉ là một ví dụ)

#include <map>
#include <iostream>

using std::map;
using std::cout;

class myClass{
 public:
 static map<int,int> create_map()
    {
      map<int,int> m;
      m[1] = 2;
      m[3] = 4;
      m[5] = 6;
      return m;
    }
 const static map<int,int> myMap;

};
const map<int,int>myClass::myMap =  create_map();

int main(){

   map<int,int> t=myClass::create_map();
   std::cout<<t[1]; //prints 2
}

6
Một hàm chắc chắn có thể được sử dụng để khởi tạo một đối tượng const.

Trong mã của OP static map<int,int> myMap = create_map();không chính xác.
Prasoon Saurav

3
Mã trong câu hỏi là sai, tất cả chúng tôi đồng ý với điều đó, nhưng nó không liên quan gì đến 'biểu thức hằng số' như bạn nói trong câu trả lời này, mà là do bạn chỉ có thể khởi tạo các thành viên tĩnh không đổi của một lớp trong khai báo nếu chúng thuộc kiểu số nguyên hoặc kiểu enum. Đối với tất cả các kiểu khác, việc khởi tạo phải được thực hiện trong định nghĩa thành viên chứ không phải khai báo.
David Rodríguez - dribeas

Câu trả lời của Neil biên dịch với g ++. Tuy nhiên, tôi nhớ đã gặp một số vấn đề với cách tiếp cận này trong các phiên bản trước của chuỗi công cụ GNU. Có một câu trả lời đúng phổ quát?
Basilevs

1
@Prasoon: Tôi không biết trình biên dịch nói gì, nhưng lỗi trong mã câu hỏi đang khởi tạo thuộc tính thành viên không đổi của kiểu lớp trong khai báo lớp, bất kể việc khởi tạo có phải là biểu thức hằng hay không. Nếu bạn định nghĩa một lớp: struct testdata { testdata(int){} }; struct test { static const testdata td = 5; }; testdata test::td;nó sẽ không biên dịch được ngay cả khi việc khởi tạo được thực hiện với một biểu thức hằng ( 5). Nghĩa là, 'biểu thức hằng' không liên quan đến tính đúng đắn (hoặc thiếu nó) của mã ban đầu.
David Rodríguez - dribeas

-2

Tôi thường sử dụng mẫu này và khuyên bạn cũng nên sử dụng nó:

class MyMap : public std::map<int, int>
{
public:
    MyMap()
    {
        //either
        insert(make_pair(1, 2));
        insert(make_pair(3, 4));
        insert(make_pair(5, 6));
        //or
        (*this)[1] = 2;
        (*this)[3] = 4;
        (*this)[5] = 6;
    }
} const static my_map;

Chắc chắn nó không dễ đọc lắm, nhưng nếu không có các lib khác thì tốt nhất là chúng ta có thể làm được. Ngoài ra, sẽ không có bất kỳ thao tác thừa nào như sao chép từ bản đồ này sang bản đồ khác như trong nỗ lực của bạn.

Điều này thậm chí còn hữu ích hơn bên trong các hàm: Thay vì:

void foo()
{
   static bool initComplete = false;
   static Map map;
   if (!initComplete)
   {
      initComplete = true;
      map= ...;
   }
}

Sử dụng như sau:

void bar()
{
    struct MyMap : Map
    {
      MyMap()
      {
         ...
      }
    } static mymap;
}

Bạn không chỉ cần ở đây để xử lý biến boolean nữa, bạn sẽ không có biến toàn cục ẩn được kiểm tra nếu trình khởi tạo của biến tĩnh bên trong hàm đã được gọi.


6
Kế thừa nên là công cụ cuối cùng, không phải là công cụ đầu tiên.

Một trình biên dịch hỗ trợ RVO loại bỏ việc sao chép dư thừa với các phiên bản chức năng. Ngữ nghĩa di chuyển C ++ 0x loại bỏ phần còn lại, khi chúng có sẵn. Trong mọi trường hợp, tôi nghi ngờ nó gần như trở thành một nút cổ chai.

Roger, tôi biết rõ về RVO, && và chuyển đổi ngữ nghĩa. Đây là một giải pháp hiện nay với số lượng mã và thực thể tối thiểu. Cộng với tất cả các tính năng C ++ 0x sẽ không giúp ích gì với đối tượng tĩnh bên trong ví dụ hàm vì chúng tôi không được phép xác định các hàm bên trong các hàm.
Pavel Chikulaev
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.