Ваша проблема реальна, и нет хорошего способа обхода для C++ 11.
C++ 14 исправляет эту проблему путем добавления шаблонной перегрузки std::map::find
- соответствующее предложение - N3657.В C++ 14, ваша программа будет выглядеть следующим образом:.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <map>
#include <algorithm>
class std_string {
char *m_s;
public:
std_string() { m_s = nullptr; }
std_string(const char* s) { puts("Oops! A new std_string was constructed!"); m_s = strdup(s); }
~std_string() { free(m_s); }
std_string(std_string&& ss) = delete;
std_string(const std_string& ss) = delete;
std_string& operator=(std_string&& ss) = delete;
std_string& operator=(const std_string& ss) = delete;
bool operator< (const char* s) const { return strcmp(m_s, s) < 0; }
bool operator< (const std_string& ss) const { return strcmp(m_s, ss.m_s) < 0; }
friend bool operator< (const char* s, const std_string& ss) { return strcmp(s, ss.m_s) < 0; }
};
int main()
{
{
puts("The C++11 way makes a copy...");
std::map<std_string, int> m;
auto it = m.find("Olaf");
}
{
puts("The C++14 way doesn't...");
std::map<std_string, int, std::less<>> m;
auto it = m.find("Olaf");
}
}
(std::less<>
является обобщенным "менее чем" компаратор, что эквивалентно operator<
C++ 03 и C++ 11 имеет разбитый по -стандартная версия этого компаратора, которая заставляет оба аргумента быть одного и того же типа. C++ 14, наконец, делает это правильно.)
К сожалению, Комитет, похоже, решил, что люди должны пройти весь свой код C++ 11 и обновить каждый контейнер, чтобы использовать std::less<>
в качестве компаратора - по умолчанию это происходит не просто. Нет веских оснований для этого решения; это просто «Путь». (См. Мои комментарии выше о поломке. C++ имеет плохую привычку вводить сломанные версии вещей, прежде чем вводить «настоящие» версии несколько лет спустя.)
Для C++ 11, std::map::find
имеет только одну перегрузку (тот, который принимает const Key&
), поэтому любое обходное решение обязательно будет включать изменение типа Key
, чтобы быть менее дорогостоящим - мы не можем просто возиться с компаратором, потому что к тому времени, когда выполнение достигнет компаратора, мы уже продвинули find
аргумент Key
.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <map>
#include <algorithm>
class std_string {
char *m_s;
public:
std_string() : m_s(nullptr) { }
std_string(const char* s) : m_s(strdup(s)) { puts("Oops! A new std_string was constructed!"); }
~std_string() { free(m_s); }
std_string(std_string&& ss) : m_s(nullptr) { std::swap(m_s, ss.m_s); }
std_string(const std_string& ss) : m_s(strdup(ss.data())) { puts("Oops! A new std_string was constructed!"); }
std_string& operator=(std_string&& ss) = delete;
std_string& operator=(const std_string& ss) = delete;
const char* data() const { return m_s; }
bool operator< (const char* s) const { return strcmp(m_s, s) < 0; }
bool operator< (const std_string& ss) const { return strcmp(m_s, ss.m_s) < 0; }
friend bool operator< (const char* s, const std_string& ss) { return strcmp(s, ss.m_s) < 0; }
};
struct string_or_ptr {
union {
const char* ptr;
alignas(std_string) unsigned char str[sizeof (std_string)];
} m_u;
bool m_deep;
char const* & ptr() { return m_u.ptr; }
std_string& str() { return *reinterpret_cast<std_string*>(m_u.str); }
char const* const & ptr() const { return m_u.ptr; }
std_string const& str() const { return *reinterpret_cast<const std_string*>(m_u.str); }
string_or_ptr() : m_deep(false) { ptr() = ""; }
string_or_ptr(const char* s) : m_deep(false) { ptr() = s; }
string_or_ptr(std_string&& s) : m_deep(true) { new ((void*)&str()) std_string(std::move(s)); }
string_or_ptr(const std_string& s) : m_deep(true) { new ((void*)&str()) std_string(s); }
~string_or_ptr() { if (m_deep) str().~std_string(); }
std_string& operator=(std_string&& ss) = delete;
std_string& operator=(const std_string& ss) = delete;
operator const char*() const { return m_deep ? str().data() : ptr(); }
bool operator< (const char* s) const { return strcmp((const char*)*this, s) < 0; }
bool operator< (const std_string& ss) const { return (const char*)*this < ss; }
bool operator< (const string_or_ptr& sp) const { return strcmp((const char*)*this, (const char*)sp) < 0; }
friend bool operator< (const char* s, const string_or_ptr& sp) { return strcmp(s, (const char*)sp) < 0; }
friend bool operator< (const std_string& ss, const string_or_ptr& sp) { return ss < (const char*)sp; }
};
int main()
{
{
puts("The C++11 way...");
std::map<std_string, int> m;
auto it = m.find("Olaf");
}
{
puts("The C++11 way with a custom string-or-pointer Key type...");
std::map<string_or_ptr, int> m;
auto it = m.find("Olaf");
}
}
Если вы используете карту «строковых» ключей, то под обложками и при построении карты уже происходит много невидимых «строковых» распределений. Это действительно стоит беспокоиться? В большинстве приложений есть другие проблемы с перфорацией, которые будут выше. Вы могли бы избежать этого, возможно, изолируя магические значения в статичном классе «MyConstants». –
@SteveTownsend Большинство/все эти распределения будут выполняться при вставке, хотя если бы у вас был высокопроизводительный метод запроса карты, которая не выполняла никаких распределений, это могло бы быть ... – Benj
@Benj - 'map :: find' will не приведет к очевидному распределению в любом случае, если для ввода не требуется конструкция 'string' (как здесь, через неявное преобразование) –