Почему стандартные ассоциативно упорядоченные контейнеры допускают использование `const char *` в качестве ключа? - PullRequest
2 голосов
/ 01 октября 2019

Насколько я знаю, мы никогда не должны сравнивать две строки константных символов, используя реляционные операторы <> ... потому что факт, что он сравнивает адреса, а не значения:

const char* sz1 = "Hello";
const char* sz2 = "hello";
if(sz1 < sz2);// not valid. So use strcmp instead.
  • Что я заметил, что упорядоченные ассоциативные контейнеры, такие как map, multimap, set, multiset, накладывают ограничение на их key, так что ключ должен каким-то образом сравниваться для упорядочения элементов в контейнере. Оператором по умолчанию для ключа является < operator.

Все понятно, пока я не создаю map, set из const char*, тогда я получаю неверные результаты:

std::set<const char*> scp{ "Hello", "World", "C++", "Programming" };    
std::set<std::string> sstr{ "Hello", "World", "C++", "Programming" };

// error
std::copy(scp.cbegin(), scp.cend(), std::ostream_iterator<const char*>(std::cout, " "));
std::cout << std::endl;

// Ok 
std::copy(sstr.cbegin(), sstr.cend(), std::ostream_iterator<std::string>(std::cout, " "));
std::cout << std::endl;
  • Очевидно, что scp сравнивает указатели со строками символов, в то время как sstr в порядке, пока класс string определил < для правильной работы.

  • Почему STL позволяет это? (создание ассоциативных контейнеров с типом ключевого элемента char*) и почему здесь даже нет предупреждений?

Ответы [ 2 ]

8 голосов
/ 01 октября 2019

Оператором по умолчанию для клавиши является оператор <.

Это не так. Оператор сравнения по умолчанию для ассоциативных контейнеров без хеширования: std::less. std::less использует operator < для сравнения, но с одним ключевым отличием. В отличие от указателей, встроенных в operator <, где

, ни один указатель не требуется для сравнения больше другого.

source

std::less имеет то, что специализации

для любого типа указателя приводят к строгому общему порядку, который согласован среди этих специализаций и также соответствует частичному порядку, наложенному построеннымоператоры <, >, <=, >=.

source

Так что это безопасная операция, и мы можем надежно хранить указатели на карте.

0 голосов
/ 01 октября 2019

Как уже указывали другие, может быть, иногда вы хотите сравнения указателей, а если нет, тогда контейнер позволяет вам предоставить свой собственный оператор сравнения, например:

#include <cstring>
#include <iostream>
#include <iterator>
#include <string>
#include <set>

struct CStrCmp {
    bool operator() (const char* lhs, const char* rhs) const {
        return strcmp(lhs, rhs) < 0;
    }
};
int main()
{
    std::set<const char*, CStrCmp> scp{ "Hello", "World", "C++", "Programming" };
    std::set<std::string> sstr{ "Hello", "World", "C++", "Programming" };

    // This works too now
    std::copy(scp.cbegin(), scp.cend(), std::ostream_iterator<const char*>(std::cout, " "));
    std::cout << std::endl;

    // Ok 
    std::copy(sstr.cbegin(), sstr.cend(), std::ostream_iterator<std::string>(std::cout, " "));
    std::cout << std::endl;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...