C++ 使用std::string键避免std::map/std::无序映射查找中的临时变量

C++ 使用std::string键避免std::map/std::无序映射查找中的临时变量,c++,stl,C++,Stl,考虑以下代码: std::map<std::string, int> m1; auto i = m1.find("foo"); const char* key = ... auto j = m1.find(key); std::map m1; 自动i=m1.find(“foo”); 常量字符*键=。。。 自动j=m1.查找(键); 这将为每个地图查找创建一个临时std::string对象。避免它的标准方法是什么?不要使用指针;相反,直接传递字符串。然后,您可以利用参考资料: v

考虑以下代码:

std::map<std::string, int> m1;
auto i = m1.find("foo");

const char* key = ...
auto j = m1.find(key);
std::map m1;
自动i=m1.find(“foo”);
常量字符*键=。。。
自动j=m1.查找(键);

这将为每个地图查找创建一个临时std::string对象。避免它的标准方法是什么?

不要使用指针;相反,直接传递字符串。然后,您可以利用参考资料:

void do_something(std::string const & key)
{
    auto it = m.find(key);
    // ....
}

C++通常会变得“更正确”,你使用它的习惯用法越多,并且不尝试用它来编写C。

好吧,map的
find
实际上接受对键的恒定引用,因此你不能避免在某个点创建它

对于代码的第一部分,可以使用值为“foo”的常量static std::string进行查找。这样就不会创建副本

若你们想走斯巴达的路,你们可以创建你们自己的类型,它可以像字符串一样使用,但也可以保存指向字符串文本的指针

但在任何情况下,与地图查找相关的开销都是如此巨大,因此这并没有真正意义。如果我是你,我会先用谷歌的密集散列替换地图/无序地图。然后我会运行英特尔的VTune(现在的放大器),看看时间在哪里,并优化这些地方。我怀疑字符串作为键会出现在瓶颈前十名列表中。

看看llvm中的类

它们可以非常便宜地从c字符串、字符串文本或std::string构建。如果您制作了这些元素的映射,而不是std::string,那么构造将非常快


但这是一个非常脆弱的系统。您需要确保插入的字符串的任何源在映射的生命周期内都保持活动状态且未被修改。

您可以通过为
std::map
提供一个自定义比较器类来避免临时性,该类可以比较
char*
s。(默认值将使用指针的地址,而不是您想要的地址。您需要比较字符串的值。)

因此,类似于:

class StrCmp
{
public:
  bool operator () (const char *a, const char *b)
  {
    return strcmp(a, b) < 0;
  }
};

// Later:
std::map<const char *, int, StrCmp> m;
class StrCmp
{
公众:
布尔运算符()(常量字符*a,常量字符*b)
{
返回strcmp(a,b)<0;
}
};
//后来:
std::map m;

然后,像普通映射一样使用,但传递char*。请记住,您存储在地图中的任何内容都必须在地图期间保持活动状态。这意味着您需要char文本,或者您必须自己保持指针指向的数据处于活动状态。出于这些原因,我会使用一个
std::map
并吃临时的,直到分析显示确实需要上面的内容。

没有办法避免一个复制字符数据的临时
std::string
实例。请注意,如果您的标准库实现使用短字符串优化,此成本非常低,并且不会导致动态内存分配

但是,如果您需要频繁地代理C样式的字符串,您仍然可以提出自定义的解决方案来绕过此分配。如果您必须经常这样做,并且您的字符串足够长,无法从短字符串优化中获益,那么这会有回报

如果您只需要非常小的字符串功能子集(例如,仅分配和复制),那么您可以编写一个小的专用字符串类,用于存储
const char*
指针和释放内存的函数

 class cheap_string
 {
 public:
     typedef void(*Free)(const char*);
 private:
     const char * myData;
     std::size_t mySize;
     Free myFree;
 public:
     // direct member assignments, use with care.
     cheap_string ( const char * data, std::size_t size, Free free );

     // releases using custom deleter (a no-op for proxies).
     ~cheap_string ();

     // create real copies (safety first).
     cheap_string ( const cheap_string& ); 
     cheap_string& operator= ( const cheap_string& ); 
     cheap_string ( const char * data );
     cheap_string ( const char * data, std::size_t size )
         : myData(new char[size+1]), mySize(size), myFree(&destroy)
     {
         strcpy(myData, data);
         myData[mySize] = '\0';
     }

     const char * data () const;
     const std::size_t size () const;

     // whatever string functionality you need.
     bool operator< ( const cheap_string& ) const;
     bool operator== ( const cheap_string& ) const;

     // create proxies for existing character buffers.
     static const cheap_string proxy ( const char * data )
     {
          return cheap_string(data, strlen(data), &abandon);
     }

     static const cheap_string proxy ( const char * data, std::size_t size )
     {
          return cheap_string(data, size, &abandon);
     }

 private:
     // deleter for proxies (no-op)
     static void abandon ( const char * data )
     {
         // no-op, this is used for proxies, which don't own the data!
     }

     // deleter for copies (delete[]).
     static void destroy ( const char * data )
     {
         delete [] data;
     }
 };
class\u字符串
{
公众:
typedef void(*自由)(常量字符*);
私人:
const char*myData;
std::大小(mySize);
免费我免费;
公众:
//指导会员分配,小心使用。
廉价字符串(const char*data,std::size\u t size,Free-Free);
//使用自定义删除器(代理的无操作)发布。
~cheap_string();
//创建真实副本(安全第一)。
廉价字符串(const廉价字符串&);
廉价字符串和运算符=(常量廉价字符串和);
廉价字符串(常量字符*数据);
廉价字符串(常量字符*数据,标准::大小\u t大小)
:myData(新字符[size+1])、mySize(大小)、myFree(&destroy)
{
strcpy(myData,data);
myData[mySize]='\0';
}
常量字符*数据()常量;
常量std::size\u t size()常量;
//您需要的任何字符串功能。
布尔运算符<(常量字符串&)常量;
布尔运算符==(常量字符串&)常量;
//为现有字符缓冲区创建代理。
静态常量字符串代理(常量字符*数据)
{
返回字符串(数据、字符串(数据)和放弃);
}
静态常量字符串代理(常量字符*数据,标准::大小\u t大小)
{
返回字符串(数据、大小和放弃);
}
私人:
//代理的删除器(无操作)
静态无效放弃(常量字符*数据)
{
//没有op,这用于代理,代理不拥有数据!
}
//副本的删除器(删除[])。
静态无效销毁(常量字符*数据)
{
删除[]数据;
}
};
然后,您可以将该类用作:

 std::map<cheap_string, int> m1;
 auto i = m1.find(cheap_string::proxy("foo"));
std::map m1;
auto i=m1.find(廉价的字符串::代理(“foo”);
临时
廉价字符串
实例不会像
std::string
那样创建字符缓冲区的副本,但它保留了在标准容器中存储
廉价字符串
实例的安全副本语义


注意事项:如果您的实现没有使用返回值优化,您将需要为
代理
方法找到一种替代语法,例如带有特殊重载的构造函数(使用自定义
代理
typeála
std::nothrow
进行新的放置).

如果您可以更改映射的定义,您可以使用一个自定义比较器,该比较器直接允许
字符常量*
在哪里可以得到
字符常量*
?@Pablo:但是。。必须有人管理那些键指向的内存。。除非它们都在数据段中…@Pablo是的,你必须