2016-07-28 42 views
3

我有以下的代碼:爲什麼不能鏗鏘聲++推斷出lambda的地圖類型?

enum RelationalOperator { LT, LTE, EQ, GTE, GT }; 
std::map<RelationalOperator, bool (*)(const Point&, const Point&)> ops = { 
    { GTE, [](const Point& a, const Point& b) { return a >= b; } }, 
    { LTE, [](const Point& a, const Point& b) { return a <= b; } }, 
    { EQ, [](const Point& a, const Point& b) { return a == b; } }, 
    { GT, [](const Point& a, const Point& b) { return a > b; } }, 
    { LT, [](const Point& a, const Point& b) { return a < b; } }, 
}; 

該代碼是一個模板內而Point是模板參數。

我試着用auto更換可變ops的類型,但是鏘++說:

src/utils.hpp:47:10: error: cannot deduce actual type for variable 'ops' with type 'auto' from initializer list 

這是爲什麼?我認爲關鍵字auto適用於這類情況,類型很長且相當明顯。

+3

這不是*顯而易見你想要一個'map'而不是'multimap'或一個結構數組。 –

+0

請爲導致問題的實際代碼提供[mcve](http://stackoverflow.com/help/mcve)。 –

+0

@BoPersson我不好,我沒有考慮到其他對象可以有相同的初始化列表的事實! – rubik

回答

4

首先,每個拉姆達都有自己的類型,因此給定一組不同的lambda表達式,你不能因素他們入沒有一些手工鑄造的單一類型(通常將它們嵌入std::function<R(Args...)>對象中)。

然後,當你寫出這樣的初始化:

enum RelationalOperator { LT, LTE, EQ, GTE, GT }; 
std::map<RelationalOperator, bool (*)(const Point&, const Point&)> ops = { 
    { GTE, [](const Point& a, const Point& b) { return a >= b; } }, 
    { LTE, [](const Point& a, const Point& b) { return a <= b; } }, 
    { EQ, [](const Point& a, const Point& b) { return a == b; } }, 
    { GT, [](const Point& a, const Point& b) { return a > b; } }, 
    { LT, [](const Point& a, const Point& b) { return a < b; } }, 
}; 

究竟發生什麼事?它調用std::map<RelationalOperator, bool (*)(const Point&, const Point&)>的構造函數std::initializer_list。它也能夠推導出給定的支撐表達是這樣的地圖的初始化列表:

std::initializer_list<std::pair<RelationalOperator, bool (*)(Point const&, Point const&)>> 

然後,隱式轉換會發生你的lambda表達式。

現在,如果你寫來代替:

auto ops = { 
    { GTE, [](const Point& a, const Point& b) { return a >= b; } }, 
    { LTE, [](const Point& a, const Point& b) { return a <= b; } }, 
    { EQ, [](const Point& a, const Point& b) { return a == b; } }, 
    { GT, [](const Point& a, const Point& b) { return a > b; } }, 
    { LT, [](const Point& a, const Point& b) { return a < b; } }, 
}; 

它不能弄清楚什麼樣的對象是由支撐表達式(Tstd::initializer_list<T>)表示。這是gcc's error message非常明確:

main.cpp:8:29: error: unable to deduce 'std::initializer_list<auto>' from '{{1, 2}, {3, 4}}' 

    auto x = {{1, 2}, {3, 4}}; 

          ^

main.cpp:8:29: note: couldn't deduce template parameter 'auto' 
5

auto不適用於初始值設定項列表。同樣的初始化列表可以用來初始化一羣其他類型,例如:

std::map<int, bool (*)(const Point&, const Point&)> 
std::multimap<RelationalOperator, bool (*)(const Point&, const Point&)> 
std::vector<std::pair<int, bool (*)(const Point&, const Point&)>> 
1

還要考慮使用STL內置比較函子即std::equal_tostd::less_equalstd::greater_equalstd::lessstd::greater所有可用的<functional>頭文件。

例如爲:

#include <functional> 
#include <map> 

struct Point { 
    bool operator < (const Point &) const { /* actual implementation */ } 
    bool operator <= (const Point &) const { /* actual implementation */ } 
    bool operator > (const Point &) const { /* actual implementation */ } 
    bool operator >= (const Point &) const { /* actual implementation */ } 
    bool operator == (const Point &) const { /* actual implementation */ } 
    /* other stuff */ 
}; 

int main() { 
    enum RelationalOperator { LT, LTE, EQ, GTE, GT }; 
    std::map<RelationalOperator, std::function<bool(const Point&, const Point&)>> ops = { 
    {GTE, std::greater_equal<Point>()}, 
    {LTE, std::less_equal<Point>()}, 
    {EQ, std::equal_to<Point>()}, 
    {GT, std::greater<Point>()}, 
    {LT, std::less<Point>()}, 
    }; 
} 
+0

不知道這些,謝謝! – rubik

3

我認爲關鍵字auto是這些種情況,其中類型是漫長的,相當明顯。

該類型根本不明顯。每個lambda表達式生成一個唯一的匿名閉合類型,所以初始化列表中的每一個元素都有不同的類型:

auto ops = { 
    { GTE, lambda_type_1 }, 
    { LTE, lambda_type_2 }, 
    { EQ, lambda_type_3 }, 
    { GT, lambda_type_4 }, 
    { LT, lambda_type_5 }, 
}; 

每個支撐初始化沒有任何共同之處。沒有什麼明顯的。

初始化std::map時,有一個構造函數採用std::initializer_list<value_type>,編譯器可以將每個初始值設定項轉換爲該類型。當你用auto替換映射時,編譯器無法找出你期望它從一個不相關類型列表中推斷出的類型。

相關問題