標籤:

C++ unordered_map 中 double 作key如何在模板參數中實現?

auto comp_dbl_eq = [] (const double lhs, const double rhs) {
return abs(lhs - rhs) &< DBL_EPSILON; }; unordered_map&, decltype(comp_dbl_eq)&> dbl_map;

這麼寫編譯器一直報錯,不知道神馬原因,請指教。


問題出在lambda,每一個lambda都是獨一無二的類型,所以即使你命名了這個lambda,也無法再次給第二個需要lambda需要的地方用,於是你可以改用functional來存儲。

std::function& comp_dbl_eq = [] (const double lhs, const double rhs) {
return abs(lhs - rhs) &< DBL_EPSILON; }; unordered_map&, decltype(comp_dbl_eq)&> dbl_map;

或者也可以使用一個functor

struct KeyEqual
{
bool operator()(const double lhs, const double rhs) const
{
return abs(lhs - rhs) &< DBL_EPSILON; } }; unordered_map&, KeyEqual&> dbl_map;

使用C++11的lambda有一個非常關鍵點要記住:編譯器會給每一個lambda賦予獨一無二的類型。


問題出在:

note: a lambda closure type has a deleted default constructor。你應該同時把 comp_dbl_eq 傳遞給 dbl_map 的構造函數。


就算你編譯過了,你的寫法也達不到想要的效果。因為你認為相等的兩個double值,其hash很可能不等,因此很可能不會碰撞,根本就調用不到你的comp_dbl_eq。

怎樣判斷浮點數是否相等並保證同一性? - Milo Yip 的回答


糾正我之前的錯誤

藍色大大的回答其實是切入本質的

下面是我實現的一個簡單示例

#include &

template &&>
class Map
{
public:
Map() :
mComparator
{
[]
(_Key const a,_Key const b) -&> int
{
if (a &> b) return 1;
else if (a &< b) return -1; else return 0; } } {} template &
Map(__Comparator comparator) :
mComparator{std::forward&<__Comparator&>(comparator)}
{}

private:
_Comparator mComparator;
};

auto comp = [] (double const a,double const b) -&> int
{
return 0;
};

int main()
{
Map& map;//正確

//Map& map1; 錯誤
Map& map1{comp};//正確

Map& map2//正確
{
[] (double const a,double const b) -&> int
{
return 0;
}
};

return 0;
}

注意之所以

Map& map1;

這是錯誤的

是因為展開後的代碼等價於

class Map
{
public:
Map() :
mComparator
{
[]
(_Key const a,_Key const b) -&> int
{
if (a &> b) return 1;
else if (a &< b) return -1; else return 0; } } {} private: decltype(comp) mComparator; };

這裡必須用一個默認比較方法 初始化mComparator 因為模版只給出了類型 使用時需要創建實例

這裡decltype(comp)類型的mComparator通過

[] (_Key const a,Key const b) -&> int
{
if (a &> b) return 1;
else if (a &< b) return -1; else return 0; }

對其進行初始化

可以直接理解成

auto comp = [] (double const a,double const b) -&> int
{
return 0;
};

decltype(comp) mComparator
{
[] (_Key const a,Key const b) -&> int
{
if (a &> b) return 1;
else if (a &< b) return -1; else return 0; } };

接下來我們去除噪音 簡化之後代碼如下

auto lambda = [] () {};
decltype(lambda) lambda2 = [] () {};//這是錯誤的

就像藍色所說的 每個lambda都是一個獨立的類型

也就是說錯誤的原因是因為

第二句中[] () {}的類型和decltype(lambda)是不同的

所以無法通過[] () {}構造lambda2

這裡類比一下

auto i = int{0};
decltype(i) i2 = int{0};//完全正確

很奇怪吧 所以我一開始想當然的以為 相同捕獲和簽名的lambda應該是相同的類型

我還是不理解為什麼標準要讓他們成為不同的類型

如何讓代碼正確呢?

請看下面代碼

auto lambda = [] () {};
decltype(lambda) lambda2 = lambda;//這樣就可以通過

請注意這裡 lambda 就和decltype(lambda)是相同的類型 所以可以通過lambda構造lambda2

這就是

Map& map1{comp};

正確的原因

因為這裡直接調用的是第二個構造函數

效果等同於上面的代碼

類型是一樣的 所以可以通過

所以LZ只需要把代碼修改成

auto comp_dbl_eq = [] (const double lhs, const double rhs)
{
return abs(lhs - rhs) &< DBL_EPSILON; }; std::unordered_map&,decltype(comp_dbl_eq)&> dbl_map
{
10,//桶的大小
std::hash&{},//散列器
comp_dbl_eq,//這裡傳入比較器的實例 通過這個實例去初始化map內部的比較器就就沒有問題了
};

不過LZ的寫法完全是多此一舉

這樣的寫法完全喪失了lambda的匿名優勢

正確的寫法應該是我示例代碼的第三種方式

std::unordered_map& dbl_map
{
10,//桶的大小
std::hash&{},//散列器
[] (const double lhs, const double rhs)
{
return abs(lhs - rhs) &< DBL_EPSILON; } };


所有浮點運算都應當定義運算的精度,一旦確定了精度,所有浮點的問題自然變成了字元串問題或者一定乘法因子下的整數問題。


推薦閱讀:

C++ std::set 的實現中對於iterator的這個強制轉換是如何進行的?
C# 為什麼去掉了C++中頭文件的概念?
如何使用C++編寫一個模板,可以同時適用於數組和vector<int>類型且避免數據的複製?
C++獲取數組長度只返回1?
構造函數不能是虛函數?

TAG:C |