2013-08-28 44 views
6

Giả sử tôi có std::map<std::string, T> (hoặc unordered_map) và tôi muốn truy cập khóa từ trình lặp/tham chiếu/con trỏ đến nội dung.Tránh lưu trữ bản sao khóa bản đồ

Có cách nào để thực hiện điều đó mà không có hai bản sao của khóa std::string (thuộc sở hữu của bản đồ, một bên trong đối tượng nội dung) không? Người ta có thể là một tham chiếu đến người khác không?

+1

Cách sử dụng 'std :: set ' trong đó 'C' so sánh chuỗi được lưu trữ trong' T'? –

+0

@Daniel: Nhưng sau đó tôi cần phải cung cấp toàn bộ 'T' đối tượng để tra cứu, và không chỉ là một' std :: string', phải không? Hoặc có thể 'C' bị quá tải để so sánh' T' với 'T' và cũng' T' thành 'std :: string'? –

+0

Ahh, tôi thấy rằng C++ 14 sẽ thêm một thành viên 'find' được tìm kiếm để tìm kiếm trên bất kỳ loại nào có thể so sánh với' T'. –

Trả lời

-1

Cả hai có thể là tham chiếu đến cùng một giá trị. Ví dụ:

#include <stdio.h> 
#include <string> 
#include <map> 

struct xx { std::string mykey; int value; }; 

int main (int argc, char **argv) 
{ 
    std::string      key1("the-key"); 
    std::map<std::string, xx>  map; 

    map[key1].mykey = key1; 
    map[key1].value = 13; 

    std::string &lookup_key = map[key1].mykey; 

    printf("%d\n", map[lookup_key].value); 
} 
+0

Tôi nghĩ bạn hiểu sai câu hỏi. Nếu tôi có 'auto & content = map [key1];', tôi cần có thể lấy lại từ 'content' sang' key1'. Một cách là đặt một bản sao của khóa bên trong loại nội dung. Tôi hỏi nếu có một cách để tránh có một bản sao. –

+0

Ồ, làm rõ điều đó. Cách duy nhất là đi bộ toàn bộ bộ sưu tập để tìm nội dung có cùng địa chỉ. Hoặc giữ một bộ sưu tập thứ hai cung cấp bản đồ theo địa chỉ, hoặc một số nội dung khác của T. – ash

+0

Bằng cách này, điều này dẫn tôi đến câu trả lời như tôi đã làm: "Có thể là một tham chiếu đến người khác không?" – ash

1

Tại sao bạn không tạo ra hai đối tượng:

std::set<std::string> wordSet; 
std::map<std::string*, T> yourMap; 

T phải chứa con trỏ đến std :: string, và yourMap cần so sánh tùy chỉnh. Ngoài ra, bạn có thể bọc tất cả những thứ này trong một số lớp học.

3

Bạn có cân nhắc sử dụng boost :: bimap không? Dưới đây là một ví dụ đơn giản:

#include <boost/bimap.hpp> 
#include <string> 
struct Person 
{ 
    Person() 
    {} 
    Person(const std::string& f, const std::string& l, int a) : first(f), last(l), age(a) 
    {} 
    std::string first; 
    std::string last; 
    int age; 
}; 

bool operator <(const Person& lhs, const Person& rhs) 
{ 
    if(lhs.last < rhs.last) 
     return true; 
    return false; 
} 

std::ostream& operator << (std::ostream& os, const Person& p) 
{ 
    os << "First Name: " << p.first << " Last Name: " << p.last << " Age: " << p.age; 
    return os; 
} 

int main() 
{ 
    typedef boost::bimap<std::string, Person> people; 
    typedef people::value_type value; 

    people m; 
    m.insert(value("12345",Person("fred", "rabbit", 10))); 
    m.insert(value("67890",Person("benjamin", "bunny", 12))); 

    Person p = m.left.at("12345"); 
    std::cout << "Person with serial no. 12345 is: " << p << "\n"; 
    std::cout << "Serial number of " << p << " is: " << m.right.at(p) << "\n"; 

} 
+0

Điều đó có thể hoạt động, nhưng tôi cảm thấy nó được thiết kế cho trường hợp bạn chỉ có một bản sao của phần tử 'mapped_type', có thể được tổng hợp, không có siêu dữ liệu như một con trỏ trực tiếp. –

2

Lý do họ thực hiện khó khăn này là vì nó nguy hiểm. Bạn phải BẢO ĐẢM rằng không ai trong số std::string thành viên bị khóa sẽ không bao giờ thay đổi giá trị hoặc toàn bộ bản đồ bị vô hiệu. Thú vị, giải pháp đầu tiên xuất hiện trong đầu xuất hiện một cách điên rồ, và trông như UB, nhưng tôi tin rằng tôi rất cẩn thận với UB.

struct key_type { 
    mutable const char* ptr;  
}; 
bool operator<(const key_type& lhs, const key_type& rhs) 
{return strcmp(lhs.ptr, rhs.ptr)<0;} 

struct person { 
    std::string name; 
    int age; 
}; 
person& people_map_get(std::map<key_type, person>& map, const char* name) { 
    auto it = map.insert(name, person{name}).first; //grab, possibly insert 
    if->first.ptr = it->second.name.c_str(); //in case of insert, fix ptr 
    return it->second; 
} 
person& people_map_assign(std::map<key_type, person>& map, person p) { 
    auto pair = map.insert(name, p); //grab, possibly insert 
    auto it = pair.first;  
    if (pair.second == false) 
     it->second = std::move(p); 
    if->first.ptr = it->second.name.c_str(); //ptr probably invalidated, so update it 
    return it->second; 
} 

int main() { 
    std::map<key_type, person> people; 
    people_map_assign(people, person{"ted"}); 
    person frank = people_map_get(people, "frank"); 
} 

Như tôi hy vọng là rõ ràng, đây là điên gần UB, và rất nhiều không được khuyến khích. Về cơ bản, trong khi chèn/tìm, các điểm chính tại đối tượng tạm thời hoặc chuỗi đầu vào của bạn, và sau đó ngay khi đối tượng được chèn/tìm thấy, khóa được thay đổi để trỏ vào giá trị chứa trong thành viên chuỗi và miễn là bạn không bao giờ làm bất cứ điều gì làm mất giá trị trả lại của .c_str() trên bất kỳ đối tượng nào có chứa person, mọi thứ chỉ hoạt động gần như không hiệu quả. Tôi nghĩ.

+0

Như đã chỉ ra, tôi có thể giữ một cặp 'std :: pair *' và có quyền truy cập cả hai. Nếu không, như dribeas giải thích, chìa khóa có một vị trí bộ nhớ cố định, do đó, nội dung có thể lưu trữ một con trỏ đến khóa đó. Tôi nghĩ rằng sẽ ít điên rồ hơn nhiều. –

+0

Tôi không nghĩ rằng bạn có thể lưu trữ nó trong 'T', vì' T' sẽ không đầy đủ tại điểm khai báo, và 'cặp' yêu cầu nó phải hoàn thành. Không chờ đợi, bởi vì một con trỏ đến 'cặp' có thể không yêu cầu nó .... Tôi không chắc nó có hoạt động hay không. Nó có thể. –

+0

Tôi chắc chắn có thể giữ một 'const std :: string *' trong 'T'. Tôi thấy quan điểm của bạn về việc đặt 'value_type *' hoặc iterator bên trong một phần tử khác, nhưng tôi không cần 'std :: pair' để hoàn thành ở đó, vì tôi chỉ lưu trữ một con trỏ. –