2015-04-23 154 views
1

在下面的程序中,我將一些信息存儲在哈希表(std :: unordered_map)中,關鍵是類RectData的一個對象,關聯的值是一個元組uint,RectData,枚舉>和自定義KeyHash和KeyEqual已被定義。爲什麼使用unordered_map和tuple需要默認構造函數?

插入一個<鍵值>對沒有默認的構造函數給出了gcc 4.9.2的兩頁錯誤。第一個錯誤的行是:

visited_info[rect0] = info0; 

我再次檢查與MSVC++ 12.0,我也有錯誤消息。

當我添加默認構造函數時,編譯是可以的,並且在運行時調用默認構造函數。我無法理解爲什麼RectData類需要默認構造函數?

使用[]運算符從哈希表中檢索數據也需要編譯時的默認構造函數,但它在運行時不會被調用,爲什麼?

auto info = visited_info[rect]; 

注:與visited_info.emplace()和visited_info.find()改變代碼解決了這個問題,但沒有回答這個問題。

感謝您的回答。

完整代碼如下。

#include <boost/functional/hash.hpp> 
#include <tuple> 
#include <vector> 
#include <unordered_map> 
#include <iostream> 

using uint = unsigned int; 

enum class Direction : int { left = 0, right = 1, up = 2, down = 3, null = 4 }; 

class RectData { 
public: 
    RectData(uint width, uint height) 
     : width_(width), height_(height), datas_(width * height, 0) { 
    total_ = width_ * height_; 
    } 


    // A default constructor must be defined! 
    RectData() : RectData(0u, 0u) { 
    std::cout << "Calling the default constructor !!!" << std::endl; 
    } 

    size_t hash() const { 
    return boost::hash_value(datas_); 
    } 

    bool operator==(const RectData &rect) const { 
    return (width_ == rect.width_) && 
      (height_ == rect.height_) && 
      (datas_ == rect.datas_); 
    } 

    struct KeyHash { 
    std::size_t operator()(const RectData &rect) const { 
     return rect.hash(); 
    } 
    }; 

    struct KeyEqual { 
    std::size_t operator()(const RectData &r1, const RectData &r2) const { 
     return r1 == r2; 
    } 
    }; 

private: 
    uint width_; 
    uint height_; 
    std::vector<uint> datas_; 
    uint total_; 
}; 

using StoredInfo = std::tuple<uint, RectData, Direction>; 

int main() { 
    std::unordered_map<RectData, StoredInfo, RectData::KeyHash, 
        RectData::KeyEqual> visited_info; 

    RectData rect0(5u, 5u); 
    RectData rect1(4u, 4u); 
    RectData rect2(3u, 3u); 
    RectData rect3(2u, 2u); 

    StoredInfo info0 = std::make_tuple(10u, rect1, Direction::up); 
    StoredInfo info1 = std::make_tuple(11u, rect2, Direction::down); 
    StoredInfo info2 = std::make_tuple(12u, rect3, Direction::left); 
    StoredInfo info3 = std::make_tuple(13u, rect0, Direction::right); 


    // the line below requires a default RectData constructor!!! 
    visited_info[rect0] = info0; 

    // default RectData constructor also needed here !!! 
    visited_info[rect1] = std::move(info2); 

    // but not needed here 
    visited_info.insert(std::make_pair(rect2, info2)); 

    // and not needed here 
    visited_info.emplace(rect3, info3); 

    // but needed here and not called!!! 
    StoredInfo i1 = visited_info[rect1]; 
    std::cout << "Verify (must be 11) = " << std::get<0>(i1) 
      << std::endl; 

    // but needed here and not called!!! 
    StoredInfo &i2 = visited_info[rect2]; 
    std::cout << "Verify (must be 12) = " << std::get<0>(i2) 
      << std::endl; 


    // and not needed here 
    auto it = visited_info.find(rect3); 
    std::cout << "Verify (must be 13) = " << std::get<0>(it->second) 
      << std::endl; 

} 
+3

在'visited_info [rect0] = info0;'中,首先調用'operator []',它別無選擇,只能默認構造一個新元素並返回一個引用。然後在該新元素上調用'operator ='。與'auto info = visited_info [rect];' - 'operator []'一樣,需要一個默認構造函數,以防給定鍵的條目不存在。如果條目確實存在,則返回對現有元素的引用,並且不調用默認構造函數。 –

+0

[Using std :: map where V has no default default constructor](http:// stackoverflow。com/questions/1935139/using-stdmapk -v-where-v-has-no-useful-default-constructor) –

+1

瞭解了,謝謝你對operator []的默認構造的明確解釋。 – user3636086

回答

4
visited_info[rect0] = info0; 

好,你覺得這是什麼呢?它是well-documented,左側評估爲存儲在地圖中的項目的引用。如果該項目之前不存在,則首先默認構建該項目。

然後,您可以使用複製或移動賦值來從表達式的右側更新該默認構造的項目。

如果您想要避免現在獲得的default-construct-and-assign操作,請改爲使用emplace


NB。例如,Python的一個可能的來源是Python,其中MyObj[1]可能會轉換爲__getitem__調用,但是MyObj[1]=1轉換爲__setitem__調用。

在C++中,左側和右側表達式必須計算爲,但不知道它們所在的語句。因此,左側評估爲a您可以從中讀取或分配給它的引用 - 但該對象需要存在才能使用該引用。

相關問題