2014-08-28 72 views
6

我想用std::set將一組unique_ptr保存到我已定義的自定義對象中。我在定義集時提供自定義比較函數(以啓用深度比較)。在將元素插入到集合中時,此比較函數似乎可以正常工作,即具有等同內容的項目不會插入兩次。但是,如果我使用operator==來比較兩個集合,它似乎被忽略,即具有等價元素的集合被返回爲不相等,而我期望(願意)它是相等的(因爲我提供的自定義比較函數做了深入的比較)。使用運算符時std :: set中unique_ptr的深度比較==

僅在插入過程中使用比較函數嗎?如果是這樣,有沒有其他辦法可以讓operator==做深入的比較?

任何指針讚賞。謝謝:)

示例代碼

// 
// main.cpp 
// Test 

#include <iostream> 
#include <set> 

class Person { 
private: 
    std::string mName; 

public: 
    Person(const std::string& name); 
    virtual ~Person() {} 

    void setName(std::string& name); 
    std::string getName(); 
}; 

typedef std::unique_ptr<Person> PersonUniquePtr; 

Person::Person(const std::string& name) 
    : mName{ name } 
{ 
} 

void Person::setName(std::string& name) 
{ 
    mName = name; 
} 

std::string Person::getName() 
{ 
    return mName; 
} 

bool isLess(Person* p1, Person* p2) 
{ 
    if (p1->getName().compare(p2->getName()) == -1) 
     return true; 

    return false; 
} 

struct PersonUniquePtr_less { 
    bool operator()(PersonUniquePtr const& p1, PersonUniquePtr const& p2) const 
    { 
     return isLess(p1.get(), p2.get()); 
    } 
}; 

int main(int argc, const char* argv[]) 
{ 
    std::set<PersonUniquePtr, PersonUniquePtr_less> personSet1; 
    std::set<PersonUniquePtr, PersonUniquePtr_less> personSet2; 

    PersonUniquePtr person1 = std::make_unique<Person>("Adam"); 
    PersonUniquePtr person2 = std::make_unique<Person>("Adam"); 
    personSet1.insert(std::move(person1)); 
    personSet1.insert(std::move(person2)); 
    std::cout << "personSet1.size(): " << personSet1.size() << std::endl; //Expected 1 

    PersonUniquePtr person3 = std::make_unique<Person>("Bruce"); 
    personSet1.insert(std::move(person3)); 
    std::cout << "personSet1.size(): " << personSet1.size() << std::endl; //Expected 2 

    PersonUniquePtr person4 = std::make_unique<Person>("Adam"); 
    PersonUniquePtr person5 = std::make_unique<Person>("Bruce"); 
    personSet2.insert(std::move(person4)); 
    personSet2.insert(std::move(person5)); 
    std::cout << "personSet2.size(): " << personSet2.size() << std::endl; //Expected 2 

    std::cout << "PersonSet1:" << std::endl; 
    for (auto& person : personSet1) { 
     std::cout << person->getName() << std::endl; 
    } //Prints out Adam Bruce 

    std::cout << "PersonSet2:" << std::endl; 
    for (auto& person : personSet2) { 
     std::cout << person->getName() << std::endl; 
    } //Prints out Adam Bruce 

    bool setsAreEqual = (personSet1 == personSet2); 
    if (setsAreEqual) { 
     std::cout << "Sets are equal" << std::endl; 
    } else { 
     std::cout << "Sets are not equal" << std::endl; 
    } 

    return 0; 
} 
+0

@BartoszKP,絕對沒有重複,這個問題是關於'的std ::設置'它不使用自定義比較函數,所以'operator =='沒有問題。這裏情況不同。 – 2014-08-28 13:59:14

回答

7

的C++ 11容器的要求說a == b相當於

distance(a.begin(), a.end()) == distance(b.begin(), b.end()) 
&& equal(a.begin(), a.end(), b.begin()) 

std::equal不使用你的自定義比較,它使用operator==

您可以使用自定義謂詞調用std::equal來執行比較:

a.size() == b.size() 
&& std::equal(a.begin(), a.end(), b.begin(), 
       [](PersonUniquePtr const& p1, PersonUniquePtr const& p2) { 
       PersonUniquePtr_less cmp; 
       return !cmp(p1, p2) && !cmp(p2, p1); 
       }); 

在C++ 14它更簡單,因爲有中std::equal需要四個迭代器一個新的過載,雖然作爲TemplateRex在下面評論指出,這是比手動測試a.size() == b.size()效率較低:

std::equal(a.begin(), a.end(), b.begin(), b.end(), 
      [](PersonUniquePtr const& p1, PersonUniquePtr const& p2) { 
      PersonUniquePtr_less cmp; 
      return !cmp(p1, p2) && !cmp(p2, p1); 
      }); 

在C++ 14可以使用多態的λ節省一些打字:

std::equal(a.begin(), a.end(), b.begin(), b.end(), 
      [](auto const& p1, auto const& p2) { 
      PersonUniquePtr_less cmp; 
      return !cmp(p1, p2) && !cmp(p2, p1); 
      }); 
+0

謝謝。我在我的項目中使用了C++ 14,並且上面的代碼工作正常:) – georgemp 2014-08-28 15:11:08

+2

IIRC,兩條雙向迭代器(線性)對4段'std :: equal'調用'std :: distance',而設定的大小可以在一段時間內獲得。那麼3段過載不會在設置的元素上保存指針追蹤路徑嗎? – TemplateRex 2014-08-29 08:54:37

+0

@TemplateRex,是的,這是一個很好的觀點。編輯。 – 2014-08-29 09:45:18