あなたの懸念は本当であり、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++は、数年後に "本物の"バージョンを導入する前に、壊れたバージョンのものを導入するという悪い習慣を持っています)。
の場合、std::map::find
は1つの過負荷(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");
}
}
あなたがカバーの下に、マップの構築にSTRING'キー 'のマップを使用している場合は、目に見えない' STRING'割り当ての多くは、すでに起こっています。これは本当に心配する価値がありますか?ほとんどのアプリには、上位にランクされるその他の悩みがあります。これは、静的な "MyConstants"クラスのマジック値を分離することで回避できます。 –
@SteveTownsendほとんどの/すべての割り当ては、挿入時に起こります。割り当てが行われていないマップをクエリする高性能な方法があれば、それはきちんとしているかもしれません... – Benj
@Benj - 'map :: find'は(暗黙の変換を介して) –