“在没有模板参数的情况下使用的类 std::map”错误

Posted

技术标签:

【中文标题】“在没有模板参数的情况下使用的类 std::map”错误【英文标题】:"class std::map used without template paramaters" error 【发布时间】:2009-07-22 05:42:12 【问题描述】:

我不得不说我不是使用 STL 的专家。这是我的问题,我有一个名为 LdapClientManager 的类,它维护着许多由 ID 管理的 LDAP 客户端。持有 LdapClients 的容器被声明为成员变量,即

typedef std::map<int, LdapClient *> LdapClientMap;
LdapClientMap _ldapClientMap;

以下函数编译失败,报错:

LdapClient * LdapClientManager::getLdapClient(unsigned int templateID)

    // Do we have an LdapClient
    LdapClientMap::const_iterator it = _ldapClientMap.find(templateID);
    if (it == std::map::end) 
        // no existing client, lets create it
        LdapClient * ldapClient = new LdapClient();
        if (ldapClient == NULL) 
            // TODO: handle out of memory condition
        

        _ldapClientMap[templateID] = ldapClient;
        return ldapClient;
    

    return it->second;

不幸的是,我在编译时收到以下错误,这是什么意思。我还没有在 google 中找到解决方案。

LdapClientManager.cc:在成员函数LdapClient* LdapClientManager::getLdapClient(unsigned int)': LdapClientManager.cc:33:template 类 std::map' 中使用,没有模板参数

【问题讨论】:

请注意,new 在内存不足的情况下不会返回 NULL(除非您使用的是 VC6 等旧编译器)。它抛出 std::bad_alloc 异常。 【参考方案1】:

std::map::end 替换为_ldapClientMap.end()。 另外,new 永远不会返回 0,如果分配失败,它会抛出异常。

请注意,程序可以更短。

LdapClient * LdapClientManager::getLdapClient(unsigned int templateID)

    LdapClient *& value = _ldapClientMap[templateID];
    if (value == 0)
        value = new LdapClient();
    return value;

【讨论】:

LdapClientMap 之前的类型名?为什么?! 糟糕,已删除。如果getLdapClient 是一个模板,这将是必要的。谢谢,EFraim。 在 std::map 中,operator[] 是一个变异操作。这可能会限制使用(不能在常量方法中使用),或者可能会影响系统(稍后调用 find() 将在数组中产生一个迭代器,每次失败的测试映射都会在内存中增长...... ) 还要考虑到,即使是像指针这样的 pod,也不会被初始化为 0,所以你检查何时创建新的客户端对象可能是错误的 CsTamas, map's operator[] 将值初始化为零。代码运行良好。【参考方案2】:

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

但你的意思可能是_ldapClientMap.end()。每张地图都有自己的终点; end 不是静态函数,因此需要在实例上调用它。 如果它是静态的,则需要提供模板参数,就像定义类型时一样:std::map&lt;int, LdapClient*&gt;::end

【讨论】:

【参考方案3】:

std::map::end() 是容器实例的成员函数,而不是通用值,因此您需要检查 std::map::find() 与 _ldapClientMap.end( )。

另外几个改进代码的建议:

标准 C++ 容器具有值语义(它们想要存储实际对象而不是指向对象的指针)。如果您确实需要存储指向 LdapClients 的指针而不是 LdapClient 对象本身,我强烈建议将它们包装在适当的智能指针中,例如 boost::shared_ptr (not 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”以防万一密钥已存在于地图中。当然,如果这是您的意图。

【讨论】:

感谢您的提示,里面有一些不错的 cmets。【参考方案4】:
LdapClientMap _ldapClientMap;

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

【讨论】:

不是。小写字母前的单前导下划线仅在全局范围内保留;类成员和局部变量都很好,他正在声明一个成员变量。大写字母前的单前导下划线和标识符中任何位置的双下划线在所有范围内都保留(因此为什么所有 STL 实现都使用 _Foo 作为参数和成员名称 - 正如标准明确指出的那样,通过宏重新定义此类标识符的用户代码是 U.B. )。 我总是用一个下划线来区分成员变量。

以上是关于“在没有模板参数的情况下使用的类 std::map”错误的主要内容,如果未能解决你的问题,请参考以下文章