簡體   English   中英

對std :: binary_search的神秘限制

[英]Mystical restriction on std::binary_search

問題描述:
考慮一些具有std::string name成員的結構。 為清楚起見,我們假設它是一個struct Human ,代表人的信息。 除了name它還可以有許多其他數據成員。
讓一個容器std::vector<Human> vec ,其中對象已按name排序。 同樣為了清楚,假設所有名稱都是唯一的。
問題是 :有一些字符串nameToFind找出數組中是否存在具有此名稱的元素。

解決方案和我的進步:
明顯和自然的解決方案似乎使用std::binary_search函數執行二進制搜索。 但是存在一個問題:被搜索的元素的類型( std::string )與容器( Human )中的元素的類型不同,並且std :: binary_search需要一個規則來比較這些元素。 我嘗試用三種方式解決這個問題,如下所述。 前兩個是為了說明我的解決方案的演變和我遇到的問題。 我的主要問題涉及第三個問題。

嘗試1:將std::string轉換為Human

寫一個比較函數:

bool compareHumansByNames( const Human& lhs, const Human& rhs )
{
   return lhs.name < rhs.name;
}

然后添加一個構造函數,從std::string構造一個Human對象:

struct Human
{
   Human( const std::string& s );
   //... other methods

   std::string name;
   //... other members
};

並使用以下形式的binary_search:

std::binary_search( vec.begin(), vec.end(), nameToFind, compareHumansByNames );

似乎工作,但出現了兩個大問題:
首先,如何初始化其他數據成員但是Human::name ,尤其是在他們沒有默認構造函數的情況下? 設置魔術值可能會導致創建一個語義上非法的對象。
其次,我們必須將此構造函數聲明為非explicit以允許在算法期間進行隱式轉換。 這種不良后果眾所周知。
此外,將在每次迭代時構造這樣的臨時Human對象,這可能變得非常昂貴。

嘗試2:將Human轉換為std::string

我們可以嘗試將一個operator string ()添加到Human類,返回它的name ,然后使用兩個std::string的comparsion。 但是,由於以下原因,這種方法也很不方便:

首先,由於此處討論的問題,代碼不會立即編譯。 我們將需要更多工作來使編譯器使用適當的operator <
第二,什么意思是“將人類轉換為字符串”? 這種轉換的存在可能導致類Human語義錯誤使用,這是不合需要的。

嘗試3:比較沒有轉換。

到目前為止,我得到的最佳解決方案是創建一個

struct Comparator
{
   bool operator() ( const Human& lhs, const std::string& rhs )
   {
      return lhs.name < rhs;
   }
   bool operator() ( const std::string& lhs, const Human& rhs )
   {
      return lhs < rhs.name;
   }
};

並使用二進制搜索

binary_search( vec.begin(), vec.end(), nameToFind, Comparator() );

這個編譯和執行正確,一切似乎都沒問題,但這里有趣的部分開始:

請訪問http://www.sgi.com/tech/stl/binary_search.html 這里說“ ForwardIterator 的值類型與T的類型相同 ”。 相當混亂的限制,我的最后一個解決方案打破了它。 讓我們看看C ++標准對它的評價:


25.3.3.4 binary_search

template<class ForwardIterator, class T>
bool binary_search(ForwardIterator first, ForwardIterator last,
const T& value);

template<class ForwardIterator, class T, class Compare>
bool binary_search(ForwardIterator first, ForwardIterator last,
const T& value, Compare comp);

要求:類型T是LessThanComparable(20.1.2)。


有關ForwardIterator的類型沒有明確說明。 但是,在20.1.2中給出的LessThanComparable的定義中,關於相同類型的兩個元素的比較被稱為 這是我不明白的。 它確實意味着被搜索對象類型和容器對象類型 必須相同,我的解決方案是否打破了這一限制? 或者它不是指使用comp比較器的情況,而只是關於默認operator <用於比較的情況? 在第一種情況下,我很困惑如何使用std::binary_search來解決這個問題,而不會遇到上面提到的問題。

提前感謝您的幫助,並抽出時間閱讀我的問題。

注意:我知道手工編寫二進制搜索不會花時間並立即解決問題,但為了避免重新發明輪子,我想使用std :: binary_search。 我也很有興趣根據標准找出這種限制的存在。

如果您的目標是查找是否存在具有給定名稱的Human ,則以下內容應該可以正常工作:

const std::string& get_name(const Human& h)
{
    return h.name;
}

...

bool result = std::binary_search(
    boost::make_transform_iterator(v.begin(), &get_name),
    boost::make_transform_iterator(v.end(), &get_name),
    name_to_check_against);

[完全重寫; 無視評論]

措辭已從C ++ 03更改為C ++ 0x。 在后者中,不再需要T低於可比性,可能是為了減輕這種不必要的限制。

新標准只要求comp(e, value)暗示!comp(value, e) 因此,只要您的比較器實現兩個方向,您就應該能夠合法地使用比較器函數搜索string作為值,該比較器函數實現兩個非對稱比較(即您的“嘗試3”)。

我認為標准在這里說的是表達式fucntor(a, b)需要是一個有效的嚴格弱排序,無論算法決定做類似functor(*begin, *(begin + 1))事情。 因此,我認為你的比較器需要提供operator()(Human, Human)的重載才能符合要求。

也就是說,我認為這是標准沒有明確允許的那些事情之一,但是很少或根本沒有實現利用標准提供的寬容度

我認為標准中的任何地方都沒有要求binary_search傳遞給比較函數(或<運算符)的值的類型必須相同。 所以,我認為使用一個可以處理兩種不同類型值的比較器是完全正確的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM