代码之家  ›  专栏  ›  技术社区  ›  Magnus Hoff

习题C++中的习语C++

  •  14
  • Magnus Hoff  · 技术社区  · 17 年前

    对于一个 std::map<std::string, std::string> variables ,我想这样做:

    BOOST_CHECK_EQUAL(variables["a"], "b");
    

    唯一的问题是,在这种情况下 variables const 如此 operator[] 不起作用:

    现在,有几个解决办法;把 康斯特 使用 variables.count("a") ? variables.find("a")->second : std::string() 或者做一个函数包装它。在我看来,这些都不如 操作员[ ] . 我该怎么办?有没有一个标准的方法来做这个(漂亮)?

    编辑: 只是陈述你不想给出的答案:不,在C++中没有方便、漂亮、标准的方法。我必须实现一个支持功能。

    7 回复  |  直到 17 年前
        1
  •  11
  •   C. K. Young    17 年前
    template <typename K, typename V>
    V get(std::map<K, V> const& map, K const& key)
    {
        std::map<K, V>::const_iterator iter(map.find(key));
        return iter != map.end() ? iter->second : V();
    }
    

    根据意见改进实施:

    template <typename T>
    typename T::mapped_type get(T const& map, typename T::key_type const& key)
    {
        typename T::const_iterator iter(map.find(key));
        return iter != map.end() ? iter->second : typename T::mapped_type();
    }
    
        2
  •  11
  •   janm    17 年前

    丢弃const是错误的,因为如果map<>上的operator[]不存在默认的构造字符串,它将创建条目。如果映射实际上在不可变的存储中,那么它将失败。必须这样做,因为运算符[]返回允许赋值的非常量引用。(如M〔1〕=2)

    实现比较的快速自由功能:

    template<typename CONT>
    bool check_equal(const CONT& m, const typename CONT::key_type& k,
                        const typename CONT::mapped_type& v)
    {
        CONT::const_iterator i(m.find(k));
        if (i == m.end()) return false;
        return i->second == v;
    }
    

    如果我想些什么的话,我会考虑句法上的糖分和更新。

    即时语法结构包含一个执行map<gt;::find()的自由函数,并返回一个特殊类,该类包装map<gt;::const_迭代器,然后重载operator==()和operator!=()以允许与映射类型进行比较。所以你可以这样做:

    if (nonmutating_get(m, "key") == "value") { ... }
    

    我不相信这比:

    if (check_equal(m, "key", "value")) { ... }
    

    当然,这要复杂得多,发生的事情也不那么明显。

    包装迭代器的对象的目的是停止使用默认构造的数据对象。如果你不在乎,那就用“得到”的答案。

    为了回应有关GeT比比较更受欢迎的评论,希望找到一些未来的用途,我有以下评论:

    • 说出你的意思:调用一个名为“check_equal”的函数可以清楚地表明你在做一个没有对象创建的相等比较。

    • 我建议只有在您有需要时才实现功能。在那之前做一些事情往往是一个错误。

    • 根据具体情况,默认构造函数可能有副作用。如果你在比较,为什么要做额外的事情?

    • sql参数:null不等于空字符串。容器中缺少密钥是否与容器中存在的具有默认构造值的密钥完全相同?

    尽管如此,默认构造的对象相当于在非常量容器上使用map<>::operator[]。也许您对返回默认构造对象的get函数有一个当前的需求;我知道在过去我有这个需求。

        3
  •  5
  •   Konrad Rudolph    17 年前

    find 是惯用的形式。铸造掉 const 几乎总是个坏主意。你必须保证不执行写操作。虽然可以合理地期望在地图上进行读访问,但是规范并没有对此做任何说明。

    如果你 知道 该值存在,您当然可以放弃使用 count (这是非常低效的,无论如何,因为它意味着遍历地图两次。即使你不知道元素是否存在,我也不会使用这个。改为使用以下内容:

    T const& item(map<TKey, T> const& m, TKey const& key, T const& def = T()) {
        map<TKey, T>::const_iterator i = m.find(key);
        return i == m.end() ? def : i->second;
    }
    

    /编辑:正如Chris正确指出的,类型为的对象的默认构造 T 可以 要昂贵,尤其是因为这样做,即使实际上不需要这个对象(因为条目存在)。如果是这种情况,请不要使用 def 上述情况下的参数。

        4
  •  5
  •   Matt Price    17 年前

    有趣的是,在被接受的get实现中,模板类型发现有两种方法(获取值或返回默认构造对象的方法)。第一,你可以做被接受和拥有的事情:

    template <typename K, typename V>
    V get1(const std::map<K, V>& theMap, const K const key)
    {
        std::map<K, V>::const_iterator iter(theMap.find(key));
        return iter != theMap.end() ? iter->second : V();
    }
    

    或者,您可以使用映射类型并去掉其中的类型:

    template<typename T>
    typename T::mapped_type
    get2(const T& theMap, const typename T::key_type& key)
    {
        typename T::const_iterator itr = theMap.find(key);
        return itr != theMap.end() ? itr->second : typename T::mapped_type();
    }
    

    这样做的好处是,所传入的密钥的类型在类型发现中不起作用,并且可以隐式转换为密钥。例如:

    std::map<std::string, int> data;
    get1(data, "hey"); // doesn't compile because the key type is ambiguous
    get2(data, "hey"); // just fine, a const char* can be converted to a string
    
        5
  •  1
  •   xtofl Adam Rosenfield    17 年前

    实际上,operator[]在std::map上是一个非常量,因为如果它不存在,它会自动在map中插入一个键值对。(噢,副作用!)

    正确的方法是使用 map::find 如果返回的迭代器有效( != map.end() ,返回 second 如你所示。

    map<int, int> m;
    m[1]=5; m[2]=6; // fill in some couples
    ...
    map<int,int>::const_iterator it = m.find( 3 );
    if( it != m.end() ) {
        int value = it->second;
        // ... do stuff with value
    }
    

    能够 添加一个 map::operator[]( const key_type& key ) const 在您使用的std::map的子类中,断言要查找的键,然后返回 it->second .

        6
  •  0
  •   oz10    17 年前
    std::map<std::string, std::string>::const_iterator it( m.find("a") );
    BOOST_CHECK_EQUAL( 
                         ( it == m.end() ? std::string("") : it->second ), 
                         "b" 
                     );
    

    这对我来说不算太糟…我可能不会为此编写函数。

        7
  •  0
  •   user123548    16 年前

    跟踪Xtofl专门化地图容器的想法。以下工作是否正常?

    template <typename K,typename V>  
    struct Dictionary:public std::map<K,V>  
    {  
      const V& operator[] (const K& key) const  
      {  
        std::map<K,V>::const_iterator iter(this->find(key));  
        BOOST_VERIFY(iter!=this->end());  
        return iter->second;  
      }  
    };