2010-01-31 8 views
36

Nếu tôi mãC++, tôi có thể khởi tạo tĩnh std :: map tại thời gian biên dịch không?

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

sau đó g này ++ nói với tôi

deducing from brace-enclosed initializer list requires #include <initializer_list> 
in C++98 ‘example’ must be initialized by constructor, not by ‘{...}’ 

và làm phiền tôi một chút vì các nhà xây dựng được thời gian chạy và có thể, về mặt lý thuyết thất bại.

Chắc chắn, nếu có, nó sẽ thất bại nhanh chóng và phải làm như vậy một cách nhất quán, để tôi phải nhanh chóng xác định vị trí & khắc phục sự cố.

Nhưng, vẫn còn, tôi tò mò - là có anyway để khởi tạo bản đồ, vector, vv, tại thời gian biên dịch?


Chỉnh sửa: Tôi phải nói rằng tôi đang phát triển cho các hệ thống nhúng. Không phải tất cả các bộ vi xử lý sẽ có trình biên dịch C++ 0x. Phổ biến nhất có lẽ sẽ, nhưng tôi không muốn gặp phải một gotcha & phải duy trì 2 phiên bản của mã.

Để tăng cường, tôi chưa quyết định. Họ rất vui khi sử dụng các lớp máy hữu hạn của họ trong các hệ thống nhúng, vì vậy đó thực sự là những gì tôi đang viết mã ở đây, các lớp Event/State/Fsm.

Sigh, tôi đoán tôi nên chơi nó an toàn hơn, nhưng tôi hy vọng rằng cuộc thảo luận này hữu ích cho người khác.

+1

Kiểm tra http://www.state-machine.com/ đối với thư viện SM được nhúng. – rpg

+0

Cảm ơn, tôi biết về nó (nhưng vẫn +1 vì nó có thể giúp người khác). Nó có vẻ hơi quá nhiều đối với tôi, nhưng sau đó tôi cũng cần o/s trừu tượng, vì vậy ... có lẽ ... Có lẽ nó chỉ là không phát minh-đây hội chứng ;-) – Mawg

+1

Đây là [câu trả lời cho một tương tự như câu hỏi tràn ngăn xếp] (http://stackoverflow.com/a/1730798/758666), mà làm cho một sử dụng thông minh của một lớp mẫu và quá tải nhà điều hành. – wil

Trả lời

19

Không có trong C++ 98. C++ 11 hỗ trợ điều này, vì vậy nếu bạn bật cờ C++ 11 và bao gồm những gì g ++ gợi ý, bạn có thể.

Chỉnh sửa: từ gcc 5 C++ 11 là theo mặc định

+1

Có điều này đã bị thiếu một tính năng rất lâu. Tôi thực sự hy vọng C++ 0x hóa ra là C++ OA để chúng ta có thể bắt đầu yêu cầu điều này từ các nhà cung cấp trình biên dịch của chúng tôi. – daramarak

+1

Hm, nếu tôi đọc nó đúng, triển khai * có thể * khởi tạo tĩnh không tổng hợp quá nếu nó ngữ nghĩa không thay đổi. Nếu phân tích tĩnh không thể đảm bảo rằng, danh sách khởi tạo C++ 0x sẽ không thay đổi một điều gì. –

+0

@ daramarak: Tôi nghĩ rằng các nhà cung cấp trình biên dịch là những người viết tiêu chuẩn; v), hỗ trợ nên làm theo ngay sau khi spec trong trình biên dịch chính cho dù bạn yêu cầu nó hay không, và cho trình biên dịch nhỏ, chúc may mắn. – Potatoswatter

14

Bạn có thể sử dụng thư viện Boost.Assign:

#include <boost/assign.hpp> 
#include <map> 
int main() 
{ 
    std::map<int, char> example = 
     boost::assign::map_list_of(1, 'a') (2, 'b') (3, 'c'); 
} 

Tuy nhiên, như Neil và những người khác chỉ trong các ý kiến ​​dưới đây, khởi tạo này xảy ra trong thời gian chạy, tương tự như đề xuất của UncleBean.

+2

Kết quả không phải là 'boost :: assign' trong khởi tạo thời gian chạy. –

+0

@Neil, @gf Ah, gotcha, tôi đã bỏ lỡ điểm nó không phải là thời gian chạy của khóa học. – mloskot

12

Với C++ 0x bạn có thể cần phải sử dụng dấu ngoặc tất cả các cách (sử dụng cú pháp kiểu mới cho mỗi cặp cũng):

std::map<int, char> example = { {1,'a'}, {2, 'b'}, {3, 'c'} }; 

Những dấu ngoặc để xây dựng cặp không có ý nghĩa. Ngoài ra, bạn có thể đặt tên đầy đủ cho mỗi cặp hoặc sử dụng make_pair (như bạn làm trong C++ 98)

std::map<int, char> example = { 
    std::make_pair(1,'a'), 
    std::make_pair(2, 'b'), 
    std::make_pair(3, 'c') 
}; 

Như để tạo những trường hợp đó tại thời gian biên dịch: không. STL container tất cả đóng gói quản lý bộ nhớ thời gian chạy hoàn toàn.Tôi cho rằng, bạn chỉ thực sự có một bản đồ thời gian biên dịch với các thư viện như lập trình siêu lập trình của boost (không chắc chắn 100%, nếu nó hoàn toàn chính xác và chưa nghiên cứu điều gì có thể tốt):

using namespace boost::mpl; 
map< 
    pair<integral_c<int, 1>, integral_c<char, 'a'> >, 
    pair<integral_c<int, 2>, integral_c<char, 'b'> >, 
    pair<integral_c<int, 3>, integral_c<char, 'c'> > 
> compile_time_map; 
+0

UncleBens, ví dụ thứ hai của bạn không biên dịch, cách tiếp cận này IMO _has_ được thực hiện như tôi đã đăng trong câu trả lời của tôi. – Dmitry

+0

@Dmitry: Ví dụ thứ hai giả định -std = C++ 0x (trình biên dịch của OP xuất hiện để hỗ trợ nó, hoặc nó sẽ không nói về initializer_list). – UncleBens

+0

UncleBens, oh, ok, không biết rằng, gcc của tôi là một chút cũ, không có C + + 0x cho tôi được nêu ra. – Dmitry

4

với pre-C++ 0x, điều gần gũi nhất bạn có thể nhận được là bằng cách không sử dụng các container được thiết kế để sử dụng thời gian chạy (và giới hạn mình vào loại cơ bản và cốt liệu):

struct pair { int first; char second; }; 
pair arr[] = {{1,'a'}, {2,'b'}}; // assuming arr has static storage 

này có thể sau đó được truy cập bằng cách sử dụng một số thân nhân d của chế độ xem bản đồ hoặc bạn có thể triển khai trình bao bọc cho phép khởi tạo tổng hợp tương tự như những gì Boost.Array thực hiện.

Tất nhiên, câu hỏi là lợi ích hợp lý cho thời gian thực hiện điều này.

Nếu đọc sách của tôi là đúng đây, C++ 0x initializer-danh sách thể cung cấp cho bạn khởi tạo tĩnh của phi uẩn như std::mapstd::pair, nhưng chỉ khi điều đó không thay đổi ngữ nghĩa so với khởi động.
Do đó, có vẻ như tôi bạn chỉ có thể nhận được những gì bạn yêu cầu nếu việc triển khai của bạn có thể xác minh thông qua phân tích tĩnh rằng hành vi không thay đổi nếu map được khởi tạo tĩnh, nhưng không đảm bảo điều đó xảy ra.

1

Có một mẹo bạn có thể sử dụng, nhưng chỉ khi dữ liệu này sẽ không được sử dụng trong bất kỳ hàm tạo tĩnh nào khác. Đầu tiên xác định một lớp đơn giản như thế này:

typedef void (*VoidFunc)(); 
class Initializer 
{ 
    public: 
    Initializer(const VoidFunc& pF) 
    { 
     pF(); 
    } 
}; 


Sau đó, sử dụng nó như thế này:

std::map<std::string, int> numbers; 
void __initNumsFunc() 
{ 
    numbers["one"] = 1; 
    numbers["two"] = 2; 
    numbers["three"] = 3; 
} 
Initializer __initNums(&__initNumsFunc); 


Tất nhiên đây là một chút quá mức cần thiết vì vậy tôi sẽ khuyên bạn sử dụng nó chỉ khi bạn thực sự phải.

+8

Dấu gạch dưới kép được dành riêng cho trình biên dịch. – GManNickG

+1

Tôi nghĩ đó chỉ là một biến thể khác của đề xuất C++ 0x và boost.assign. Không có gì biên dịch thời gian ở đây, chỉ là một cách khác để thiết lập các giá trị trong một thể hiện bản đồ toàn cục. – UncleBens

+0

Tôi đã sử dụng dấu gạch dưới chỉ để làm cho nó rõ ràng rằng bạn sẽ không muốn chạm vào chức năng hoặc đối tượng trong mã còn lại. (bạn cũng có thể sử dụng không gian tên chưa đặt tên) Và không có gì biên dịch về nó, nhưng như tôi đã nói, đó là một mẹo - dữ liệu sẽ được khởi tạo trước khi main() –

32

Nó không chính xác khởi tạo tĩnh, nhưng vẫn, hãy thử. Nếu trình biên dịch của bạn không hỗ trợ C++ 0x, tôi muốn đi cho constructor lặp std :: bản đồ của:

std::pair<int, std::string> map_data[] = { 
    std::make_pair(1, "a"), 
    std::make_pair(2, "b"), 
    std::make_pair(3, "c") 
}; 

std::map<int, std::string> my_map(map_data, 
    map_data + sizeof map_data/sizeof map_data[0]); 

này là khá dễ đọc, không đòi hỏi bất kỳ thư viện thêm và nên làm việc trong tất cả các trình biên dịch.

+7

Lưu ý rằng cách tiếp cận này sẽ xây dựng bộ nhớ để giữ mỗi std tạm thời :: cặp các mục trong map_data của bạn, sao chép các chuỗi được khai báo tĩnh thành pair :: second, sau đó sao chép văn bản một lần nữa vào std :: map và cuối cùng là hủy các thời gian. Đối với một số lượng lớn các mục được khai báo tĩnh, điều này sẽ khá lãng phí. – Armentage

1

Không có cách tiêu chuẩn để khởi tạo std::map lúc biên dịch. Như những người khác đã đề cập, C++ 0x sẽ cho phép trình biên dịch tối ưu hóa việc khởi tạo là tĩnh nếu có thể, nhưng điều đó sẽ không bao giờ được đảm bảo.

Hãy nhớ rằng, mặc dù, STL chỉ là thông số giao diện. Bạn có thể tạo các thùng chứa tương thích của riêng mình và cung cấp cho chúng khả năng khởi tạo tĩnh.

Tùy thuộc vào việc bạn có kế hoạch nâng cấp trình biên dịch và triển khai STL hay không (đặc biệt là trên nền tảng nhúng), bạn thậm chí có thể tìm hiểu về triển khai bạn đang sử dụng, thêm các lớp dẫn xuất và sử dụng chúng!

+0

Không thể thực hiện bất kỳ 'std :: map' nào để tuân thủ và hỗ trợ khởi tạo tĩnh. Các yêu cầu hợp lệ của 'swap()' iterator làm cho nó hoàn toàn cần thiết để sử dụng lưu trữ động, cho 'std :: map' và tất cả các thùng chứa tiêu chuẩn khác ngoại trừ' std :: array'. –

+0

@BenVoigt Câu trả lời này dường như dựa trên quan niệm sai lầm về kết nối giữa niềng răng và tĩnh. Tuy nhiên, một trình biên dịch có thể đặt 'const map >>' các nút trong ROM, miễn là 'std :: allocator' không chuyên biệt.Nếu trình biên dịch có thể phát hiện số nút deallocation đó đến một destruct nhỏ và một 'free', và nó biết rằng' free' bỏ qua các địa chỉ ROM, sau đó nó có thể bỏ qua 'malloc' và phân bổ ROM tĩnh. Điều đó nói rằng, chắc chắn không có trình biên dịch đã từng làm như vậy. (Q này là ít 'const', nhưng nguyên tắc là như nhau.) – Potatoswatter

-2
template <const int N> struct Map { enum { value = N}; }; 
template <> struct Map <1> { enum { value = (int)'a'}; }; 
template <> struct Map <2> { enum { value = (int)'b'}; }; 
template <> struct Map <3> { enum { value = (int)'c'}; }; 

std::cout << Map<1>::value ; 
+5

Đó không thực sự là một bản đồ, và chắc chắn không phải là một std :: bản đồ mà câu hỏi đã được về. – Kleist