【问题标题】:c++ : unordered map with pair of string_viewesc ++:带有一对string_view的unordered_map
【发布时间】:2021-06-21 16:05:36
【问题描述】:

这是我的代码 sn-p:

    struct PairHasher {
        size_t operator()(const std::pair<std::string_view, std::string_view>& stop_stop) const {
            return hasher(stop_stop.first) + 37*hasher(stop_stop.second);
        }
        std::hash<std::string_view> hasher;
    };


BOOST_FIXTURE_TEST_CASE(unordered_map_string_view_pair_must_be_ok, TestCaseStartStopMessager)
{
    const std::vector<std::string> from_stops = {"from_0", "from_1", "from_2"};
    const std::vector<std::string> to_stops = {"to_0", "to_1", "to_2"};

    std::unordered_map<std::pair<std::string_view, std::string_view>, std::int32_t, TransportCatalogue::PairHasher> distance_between_stops;
    for ( std::size_t idx = 0; idx < from_stops.size(); ++idx) {
        std::cout << from_stops[idx] << " : " << to_stops[idx] << std::endl;
        distance_between_stops[std::pair(from_stops[idx], to_stops[idx])] = idx;
    }

    std::cout << "MAP CONTENT :" << std::endl;
    for (auto const& x : distance_between_stops)
    {
        std::cout << x.first.first << " : " << x.first.second << std::endl;
    }
}

我希望在容器内看到 3 对,但输出中只有 1 对:

MAP CONTENT :
from_2 : to_2

那么,还有两对在哪里丢失了?我做错了什么?

【问题讨论】:

    标签: c++11 string-view


    【解决方案1】:

    将我的评论移至答案。

    这很狡猾。我在Compiler Explorer 中注意到了这种变化:

    distance_between_stops[std::pair(from_stops[idx], to_stops[idx])] = idx;
    

    distance_between_stops[std::pair(std::string_view{from_stops[idx]}, std::string_view{to_stops[idx]})] = idx;
    

    修复了错误。这暗示问题在于一些隐式的string -> string_view 转换。确实如此,但它隐藏在一个额外的层后面。

    std::pair(from_stops[idx], to_stops[idx]) 创建一个std::pair&lt;std::string, std::string&gt;,但distance_between_stops 需要一个std::pair&lt;std::string_view, std::string_view&gt;。当我们将值插入映射时,这种转换会通过重载 #5 here 隐式发生:

    template <class U1, class U2>
    constexpr pair(pair<U1, U2>&& p);
    
    1. std::forward&lt;U1&gt;(p.first) 初始化first,用std::forward&lt;U2&gt;(p.second) 初始化第二个。
    • 当且仅当std::is_constructible_v&lt;first_type, U1&amp;&amp;&gt;std::is_constructible_v&lt;second_type, U2&amp;&amp;&gt; 都是true 时,此构造函数才参与重载决议。
    • 当且仅当std::is_convertible_v&lt;U1&amp;&amp;, first_type&gt;falsestd::is_convertible_v&lt;U2&amp;&amp;, second_type&gt;false 时,此构造函数为explicit

    (作为参考,std::is_constructible_v&lt;std::string_view, std::string&amp;&amp;&gt;std::is_convertible_v&lt;std::string&amp;&amp;, std::string_view&gt; 都是 true,所以我们知道这个重载是可行的和隐含的。)

    看到问题了吗?当我们使用映射的operator[] 时,它必须进行隐式转换以创建具有正确类型的键。这种隐式转换构造了一对string_views,它们正在查看来自本地strings 对的临时内存,而不是vector 中的底层字符串。换句话说,它在概念上类似于:

    std::string_view foo(const std::string& s) {
        std::string temp = s + " foo";
        return temp;
    }
    int main() {
        std::string_view sv = foo("hello");
        std::cout << sv << "\n";
    }
    

    Clang 会针对这个小示例发出警告,但不是 OP 的完整示例,这很不幸:

    warning: address of stack memory associated with local variable 'temp' returned [-Wreturn-stack-address]
        return temp;
               ^~~~
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-30
      • 1970-01-01
      • 2015-07-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多