std::set::find dla własnej klasy/struktury

std::set::find dla własnej klasy/struktury
Mc_Hammer
  • Rejestracja:ponad 17 lat
  • Ostatnio:ponad rok
0

Witam,

Mam strukturę, do której zapisuję dane pojedynczego pliku z folderu ze skanami. Wszystko trzymam w std::set<ScannedFile> files. W trakcie programu zmieniam ich nazwy i na koniec zapisuję. W jaki sposób przeładować w moim przypadku funkcję find tak żeby wyszukiwanie odbywało się zarówno dla old_name jak i new_name ?

Kopiuj
class ScannedFile 
{
public:
	std::wstring old_name{ L"" };
	mutable std::wstring new_name{ L"" };

	ScannedFile() {}

	ScannedFile(const std::wstring &old_n, const std::wstring &new_n, const std::string &ext) :
		old_name(old_n), new_name(new_n), extension(ext) 
	{
		counter++;
		if (new_n.empty())
			this->new_name = L"pusta_nazwa" + std::to_wstring(counter);
	}
	
	void SetExtension(const std::string &s);
	std::wstring GetExtension() const;

	bool operator<(const ScannedFile &f) const 
	{
		return (this->old_name < f.old_name) && (this->new_name < f.new_name);
	}

private:
	static int counter;
	std::string extension;
};

Dodam, że takie przeładowanie operatora " < " działa (blokuje dodanie) gdy chcę wstawić do files (istniejący już tam) plik skanu funkcją insert, dlaczego więc nie działa dla find lub count ?

rrowniak
  • Rejestracja:ponad 6 lat
  • Ostatnio:3 miesiące
  • Postów:82
2

Podejrzewam, że problem jest w operatorze <. Operator < musi spełniać kilka warunków (strict weak ordering):

  • ~ (x < x): element nie może być mniejszy od samego siebie
  • x < y => ~ (y < x): jeśli x jest mniejszy od y to y nie może być mniejszy od x
  • x < y i y < z => x < z: jeśli x jest mniejszy od y i y jest mniejsze od z to x jest mniejsze od z

U Ciebie jest problem z drugim warunkiem, choć nie wprost - można znaleźć dwa takie elementy (różne), że x < y zwróci false i y < x zwróci również false. Operator == (równoważności) używany w find i count jest zdefiniowany następująco: x == y <==> !(x < y) && !(y < x). To tłumaczy anomalie. Więcej szczegółów tutaj: https://en.cppreference.com/w/cpp/named_req/Compare


kq
Moderator C/C++
  • Rejestracja:prawie 12 lat
  • Ostatnio:4 minuty
  • Lokalizacja:Szczecin
3

Mc_Hammer
  • Rejestracja:ponad 17 lat
  • Ostatnio:ponad rok
0

Dzięki za odpowiedzi. std::tie pomogło.

ps: jakiś czas temu czytałem już właśnie ten zalinkowany wpis @kq, ale o nim kompletnie zapomniałem... Ech :)

Mc_Hammer
  • Rejestracja:ponad 17 lat
  • Ostatnio:ponad rok
0

Zbyt szybko krzyknąłem hurra.
@rrowniak ma rację. W moim przypadku istnieje taka możliwość, że x < y zwróci false i y < x
np.

Kopiuj
//ad hoc: using namespace std; oraz konstruktor bez trzeciego argumentu

	auto files = set<ScannedFile> {ScannedFile(L"001", L""), ScannedFile(L"002", L"")};
	
	if(!files.insert(ScannedFile(L"001", L"123")).second)
		cout << "insertion failed" << endl;
	else
		cout << "insertion succeeded" << endl;

wynik to dodanie tego pliku do kontenera, a powinno zwrócić false, ponieważ "001" już w nim jest. Dopiero wywołanie np.

Kopiuj
files.insert(ScannedFile(L"", L"123")).second

powinno zwrócić sukces.

Pewnie muszę napisać jakiś własny typ porównujący, tylko pytanie jakich warunków użyć, żeby niezależnie od siebie oba pola były traktowane jako unikatowe ?

EDIT:

Chyba udało mi się znaleźć rozwiązanie, ale jeszcze je testuję :)
Wklejam poniżej, może komuś się przyda

Kopiuj
class SFilesComp
{
	public:
	bool operator()(const ScannedFile &lhs, const ScannedFile &rhs)
	{
		bool ret, ret2;

		ret = lhs.old_name.compare(rhs.old_name) >= 0 ? false : true;
		ret2 = lhs.new_name.compare(rhs.new_name) >= 0 ? false : true;
		
		return ret && ret2;
	}
};
edytowany 3x, ostatnio: Mc_Hammer
kq
Moderator C/C++
  • Rejestracja:prawie 12 lat
  • Ostatnio:4 minuty
  • Lokalizacja:Szczecin
0

Ale... std::tie rozwiązuje również tę sytuację. Porządek leksykograficzny polega na tym, że pierw testowana jest pierwsza wartość. Jeśli jest ekwiwalentna, to dopiero porównywana jest druga, a jak i ta jest ekwiwalentna, to potem trzecia itd...


Mc_Hammer
  • Rejestracja:ponad 17 lat
  • Ostatnio:ponad rok
0

Nie wiem właśnie dlaczego tak nie jest.
https://ideone.com/236kaL

Wkładanie do seta "001" nie powinno przejść

Ten EDIT z poprzedniego posta też nie działa prawidłowo. Np. dla przypadku "223" >= "123" zablokuje dodanie nowego pliku.

Rozwiązaniem jest chyba poniższe przeładowanie operatora <

Kopiuj
	bool operator<(const ScannedFile &f) const 
	{
		if (this->old_name == f.old_name || this->new_name == f.new_name)
			return false;

		return this->old_name < f.old_name;

	}

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.