C++ &引用;class std::不使用模板参数的映射;错误

C++ &引用;class std::不使用模板参数的映射;错误,c++,gcc,stl,C++,Gcc,Stl,我不得不说我不是使用STL的专家。这里是我的问题,我有一个名为LdapClientManager的类,它维护许多由ID管理的LDAP客户机 typedef std::map<int, LdapClient *> LdapClientMap; LdapClientMap _ldapClientMap; 不幸的是,我在编译时遇到了以下错误,这是什么意思。我还没有在谷歌找到解决方案 LdapClientManager.cc:在成员函数LdapClient中* LDAPClient Man

我不得不说我不是使用STL的专家。这里是我的问题,我有一个名为LdapClientManager的类,它维护许多由ID管理的LDAP客户机

typedef std::map<int, LdapClient *> LdapClientMap;
LdapClientMap _ldapClientMap;
不幸的是,我在编译时遇到了以下错误,这是什么意思。我还没有在谷歌找到解决方案

LdapClientManager.cc:在成员函数
LdapClient中*
LDAPClient Manager::getLdapClient(未签名的int)“”:

LdapClientManager.cc:33:
template class std::map'在没有模板参数的情况下使用

std::map::end
替换为
\u ldapClientMap.end()
。 另外,
new
从不返回0,如果分配失败,它将抛出异常

请注意,程序可以缩短很多

LdapClient * LdapClientManager::getLdapClient(unsigned int templateID)
{
    LdapClient *& value = _ldapClientMap[templateID];
    if (value == 0)
        value = new LdapClient();
    return value;
}

它的意思正是它所说的意思
std::map
是一个类模板。它本身并不是一个类。它需要模板参数,就像您在定义
LdapClientMap
类型时使用的那样。稍后,您说
std::map::end
,编译器说这也需要参数

但您的意思可能是
\u ldapClientMap.end()
。每一张地图都有自己的目的
end
不是静态函数,因此需要在实例上调用它如果它是静态的,则需要提供模板参数,就像定义类型时一样:
std::map::end
std::map::end()是容器实例的成员函数,而不是通用值,因此需要对照_ldapClientMap.end()检查std::map::find()的结果

另外两条改进代码的建议:

  • 标准C++容器具有值语义(它们希望存储实际对象而不是指向对象的指针)。如果您确实需要存储指向LdapClient的指针,而不是LdapClient对象本身,我强烈建议将它们包装在一个合适的智能指针中,如boost::shared_ptr(而不是std::auto_ptr,它将不起作用)。这样,std::map的自动内存管理仍将工作,并按预期销毁对象。如果不想使用智能指针或将实际的LdapClient对象放入容器中,则必须手动管理对象的生存期,并在适当时调用delete以防止内存泄漏。我的首选是将映射的类型更改为std::map,除非LdapClient对象是多态的
  • 除非您使用的是一个非常过时的编译器,否则将常规new()的结果与0或NULL进行对比将不会产生任何新的见解,因为new在这些天由于任何原因无法分配内存时抛出std::bad_alloc
  • 而不是使用_ldapClientMap[x]=y;要插入新元素,我将使用_ldapClientMap.insert(ldapClientMap::value_type(x,y)),因为后者不会覆盖键x的现有值(前者会这样做),如果映射中已经存在键,则返回“false”。当然,如果这是你的意图的话

您应该避免使用带有前导下划线的名称。从技术上讲,它是未定义的行为,即使编译器允许它,因为使用它会与当前或将来的保留名称冲突。

请注意,new不会在内存不足的情况下返回NULL(除非您使用的是旧编译器,如VC6)。它在LdapClientMap之前抛出std::bad_alloc exception.typename?为什么?哎呀,搬走了。如果
getLdapClient
是一个模板,那么这是必需的。谢谢,EFraim。在std::map中,运算符[]是一个变异操作。这可能会限制使用(不能在常量方法中使用),也可能会影响系统(稍后调用find()将生成数组中的迭代器,对于每个失败的测试,映射将在内存中增长…)还要考虑到,即使像指针这样的pod也不会初始化为0,因此,检查何时创建新的客户端对象可能是错误的CSTAMAS,
map
operator[]
zero初始化该值。代码运行得很好,但不是。小写字母前的单个前导下划线仅在全局范围内保留;类成员和局部变量都很好,他正在声明一个成员变量。大写字母前的单前导下划线和标识符中任意位置的双下划线在所有作用域中都是保留的(因此,所有STL实现都对参数和成员名称使用
\U Foo
——正如标准明确规定的那样,通过宏重新定义此类标识符的用户代码是U.B.)。我总是使用一个下划线作为成员变量来区分它们。感谢您的提示,这里有一些很好的注释。
LdapClient * LdapClientManager::getLdapClient(unsigned int templateID)
{
    LdapClient *& value = _ldapClientMap[templateID];
    if (value == 0)
        value = new LdapClient();
    return value;
}
LdapClientMap _ldapClientMap;