Pytanie

W jaki sposób porównasz dwa następujące kontenery (typu std::vector) ?

#include <vector>
#include <string>

int main()
{
    std::vector<std::string> names1 = { "Bjarne",
                                        "Scott",
                                        "Herb",
                                        "Andre",
                                        "Rafal" };

    std::vector<char const *> names2 = { "Bjarne",
                                         "Scott",
                                         "Herb",
                                         "Andre" };

    // Implement your solution here

}

Uruchom w edytorze

Rozwiązanie

Operator ==

#include <iostream>
#include <vector>
#include <string>

int main()
{
    std::vector<std::string> names1 = { "Bjarne",
                                        "Scott",
                                        "Herb",
                                        "Andre",
                                        "Rafal" };

    std::vector<char const *> names2 = { "Bjarne",
                                         "Scott",
                                         "Herb",
                                         "Andre" };

    // Implement your solution here
    auto ans = (names1 == names2); 
    std::cout << std::boolalpha << ans << '\n';
}

Kontener typu std::vector (plik nagłówkowy vector) posiada zdefiniowany operator porównania, czyli std::vector::operator=. Działa on jednak dla kontenerów std::vector przechowujących taki sam typ. W naszym przypadku jest inaczej bowiem w kontenerze names1 przechowywane są elementy typu std::string (plik nagłówkowy string), natomiast w kontenerze names2char const *. Nie ma tutaj znaczenia, że typ char const * może zostać skonwertowany do typu std::string.  W efekcie, próba kompilacji powyższego kodu zakończy się obszernym błędem (poniżej przedstawiłem jedynie początkowy fragment):

<source>: In function 'int main()':

<source>:19:24: error: no match for 'operator==' (operand types are 'std::vector<std::__cxx11::basic_string<char> >' and 'std::vector<const char*>')

   19 |     auto ans = (names1 == names2);

      |                 ~~~~~~ ^~ ~~~~~~

      |                 |         |

      |                 |         vector<const char*>

      |                 vector<std::__cxx11::basic_string<char>>

Algorytm std::equal (dla 1,5 zakresu)

Algorytm std::equal (plik nagłówkowy algorithm) istniał już przed standardem C++11. Ma on specyficzną formę bowiem przyjmuje pełny zakres dla pierwszego kontenera (iterator początkowykońcowy) oraz iterator początkowy dla drugiego kontenera. Oznacza to, że drugi kontener musi mieć przynajmniej taki sam rozmiar jak kontener pierwszy (jest to przykład algorytmu dla tzw. ”półtora” zakresu). W naszym przypadku warunek ten nie jest spełniony bowiem kontener names2 zawiera o jeden element mniej niż kontener names1. Oznacza to, że poniższy kod źródłowy będzie cechować się niezdefiniowanym zachowaniem (ang. undefined behaviour), czego typowym objawem będzie ”crash” aplikacji:

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

int main()
{
    std::vector<std::string> names1 = { "Bjarne",
                                        "Scott",
                                        "Herb",
                                        "Andre",
                                        "Rafal" };

    std::vector<char const *> names2 = { "Bjarne",
                                         "Scott",
                                         "Herb",
                                         "Andre" };

    // Implement your solution here
    auto ans = std::equal(std::begin(names1), std::end(names1),
                          std::begin(names2));
    std::cout << std::boolalpha << ans << '\n';
}

Jeżeli jednak kontenery te zamienimy miejscami (w wywołaniu std::equal) otrzymamy nieprawidłowy rezultat (bierzemy pod uwagę tylko tyle elementów ile znajduje się w mniejszym kontenerze, jednocześnie ignorując pozostałe z większego kontenera):

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

int main()
{
    std::vector<std::string> names1 = { "Bjarne",
                                        "Scott",
                                        "Herb",
                                        "Andre",
                                        "Rafal" };

    std::vector<char const *> names2 = { "Bjarne",
                                         "Scott",
                                         "Herb",
                                         "Andre" };

    // Implement your solution here
    auto ans = std::equal(std::begin(names2), std::end(names2),
                          std::begin(names1));
    std::cout << std::boolalpha << ans << '\n';
}

Uruchom w edytorze

true

Inaczej mowiąc, przedstawiona tutaj wersja algorytmu  std::equal, będzie działać prawidłowo jedynie dla kontenerów o tych samych rozmiarach. W przeciwnym razie możesz otrzymać niezdefiniowane zachowanie lub błędny wynik.

Algorytm std::equal (dla 2 zakresów)

Standard C++14 wprowadził nową wersję algorytmu std::equal, przyjmującej pełne dwa zakresy (iterator początkowykońcowy dla obu kontenerów). Oznacza to, że algorytm ten będzie działać prawidłowo również dla kontenerów o różnych rozmiarach (w przypadku różnych rozmiarów od razu wiadomo, że kontenery są także różne). Zobaczmy  jak takie (prawidłowe) rozwiązanie sprawdza się w praktyce:

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

int main()
{
    std::vector<std::string> names1 = { "Bjarne",
                                        "Scott",
                                        "Herb",
                                        "Andre",
                                        "Rafal" };

    std::vector<char const *> names2 = { "Bjarne",
                                         "Scott",
                                         "Herb",
                                         "Andre" };

    // Implement your solution here
    auto ans = std::equal(std::begin(names1), std::end(names1),
                          std::begin(names2), std::end(names2));
    std::cout << std::boolalpha << ans << '\n';
}

Uruchom w edytorze

false

Prawidłowy output otrzymamy także po zamianie kontenerów miejscami (w wywołaniu std::equal):

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

int main()
{
    std::vector<std::string> names1 = { "Bjarne",
                                        "Scott",
                                        "Herb",
                                        "Andre",
                                        "Rafal" };

    std::vector<char const *> names2 = { "Bjarne",
                                         "Scott",
                                         "Herb",
                                         "Andre" };

    // Implement your solution here
    auto ans = std::equal(std::begin(names2), std::end(names2),
                          std::begin(names1), std::end(names1));
    std::cout << std::boolalpha << ans << '\n';
}

Uruchom w edytorze

false