2015-10-30 55 views
4

可以說我有一個字符串矢量,我想找到的所有字符串,與'a'開始,這樣我就可以做到這一點:的std :: equal_range與拉姆達

struct cmp { 
    bool operator()(const std::string &s, char c) const { return s.front() < c; } 
    bool operator()(char c, const std::string &s) const { return s.front() < c; } 
}; 
std::vector<std::string> strings; 
... 
std::sort(strings.begin(), strings.end()); 
auto range = std::equal_range(strings.begin(), strings.end(), 'a', cmp{}); 
... 

這種方法很容易出錯,因爲很容易犯錯(例如我認爲它應該是第二種方法中的c < s.front())並且有代碼重複。

那麼有可能使用泛型lambda來實現比較函數而不是使用2種方法的結構?

更通用的問題,爲什麼值來比較已經被作爲參數傳遞給std::lower_boundstd::upper_boundstd::equal_range通過時,它可以很容易地通過拉姆達被捕獲或傳遞給比較結構,然後這個問題不會在那裏呢?

如果std::equal_range不需要價值,它如何工作?

struct cmp { 
    cmp(char lc) : c(lc) {} 
    bool operator()(const std::string &s) const { return s.front() < c; } 
    char c; 
}; 
std::vector<std::string> strings; 
... 
std::sort(strings.begin(), strings.end()); 
auto range = std::equal_range(strings.begin(), strings.end(), cmp{'a'}); 
+0

如果你願意通過一個'''代替''a'',那麼一個lambda採取兩個字符串和比較他們的第一個字符將工作。否則,具有兩個重載的命名類看起來像對我來說是最乾淨的解決方案。請注意,儘管目前編寫的第二個重載是錯誤地進行比較的。 –

+0

@IgorTandetnik這很明顯,但它可能不是字符串,而是一個按部分排序的對象,所以我不想創建整個對象只是爲了將它用作關鍵字。 – Slava

+1

你可以使用'std :: partition_point'。 –

回答

4

lower_bound

std::partition_point(strings.begin(), strings.end(), 
        [](const auto& s) { return s.front() < 'a'; }); 

upper_bound

std::partition_point(strings.begin(), strings.end(), 
        [](const auto& s) { return s.front() <= 'a'; }); 

是的,這意味着你必須寫兩次調用得到equal_range相當。你可以用這個進入一個免費的模板:

template<class Iter, class T, class Proj> 
std::pair<Iter, Iter> my_equal_range(Iter first, Iter last, const T& value, Proj proj) { 
    auto b = std::partition_point(first, last, [&](const auto& s) { return proj(s) < value; }); 
    auto e = std::partition_point(b, last, [&](const auto& s) { return !(value < proj(s)); }); 
    return {b, e}; 
} 

並把它作爲

my_equal_range(strings.begin(), strings.end(), 'a', 
       [](const auto& s) { return s.front(); }); 

範圍TS工作草案增加了預測,以算法,所以你會(最終)能夠做到這個:

std::experimental::ranges::equal_range(strings, 'a', {}, 
             [](const auto& s) { return s.front(); }); 
+0

對於第二個'std: :partition_point'調用使用前一個調用的輸出,而不是'first'? – Slava

+0

@Slava當然,這樣會更有效率, –

+0

你實際上可以重用'first'和'last' :)'first = ...; last = ...;返回{first,last};' – Slava

4

要回答你的第一個問題,比較器是否可以使用通用lambda實現,是的,它可以。給定一個charstring參數,您還需要一些輔助函數來返回所需的結果。你不能簡單地已經比較捕捉值

auto get_char(char c)    { return c; } 
auto get_char(std::string const& s) { return s.front(); } 
auto cmp = [](auto const& l, auto const& r) { return get_char(l) < get_char(r); }; 

Live demo


一個原因是因爲equal_range兩個重載然後可以是模糊的,你需要一個稍微不同的名字或一些其他的方式(例如,標籤參數)來消除這兩者的歧義。

+0

好的解決方案,但不完美:(它有相同的問題,阻止我在lambda之前使用算法 - 你必須在代碼使用範圍之外編寫函數 – Slava