【问题标题】:Why does std::map::const_iterator call the std::pair constructor during a std::for_each, but a simple for loop does not?为什么 std::map::const_iterator 在 std::for_each 期间调用 std::pair 构造函数,而简单的 for 循环却没有?
【发布时间】:2014-03-31 15:09:21
【问题描述】:

我有一个类的稍微复杂的数据成员,如下所述:

class BranchOutputRow
{
...
}

class Foo
{

public:

    // Slightly complex data member here
    std::map<boost::multiprecision::cpp_int, std::set<BranchOutputRow>> hits;

    void DoLoop1()
    {
        // This loop calls the std::pair<> constructor

        std::for_each(hits.cbegin(), hits.cend(),
        [&](std::pair<boost::multiprecision::cpp_int,
                      std::set<BranchOutputRow>> const & hit)
        {
            ...
        }
    }

    void DoLoop2()
    {
        // This loop does NOT call the std::pair<> constructor

        for (std::map<boost::multiprecision::cpp_int,
                      std::set<BranchOutputRow>>::const_iterator hitsPtr 
                    = hits.cbegin();
         hitsPtr != hits.cend();
         ++hitsPtr)
         {
             ...            
         }
    }

}

int main()
{
    Foo foo;
    foo.hits[1] = std::set<BranchOutputRow>();
    foo.hits[1].insert(BranchOutputRow());

    foo.DoLoop1(); // direct access to map object is not available
    foo.DoLoop2(); // direct access to map object is available
}

如前所述,我发现 Loop #1 调用了 std::pair 构造函数,尽管 lambda 函数通过引用接受其参数。因此,在循环 1 中,我无法直接访问地图中的对象,而只能访问副本。在我的实际程序中,我需要直接访问;因此,我必须使用 Loop 2 指示的版本。

(事实上,循环 2 调用std::pair 构造函数 - 这并不奇怪 - 并且确实提供了对地图中对象的直接访问。)

我认为 std::for_each 会经过精心设计,以提供与循环 2 等 for 循环相同的语义,因此不会调用 std::pair 构造函数,而是允许直接访问地图。

为什么循环 1 调用 std::pair 构造函数,尽管 lambda 函数通过引用接受其参数?

【问题讨论】:

  • 密钥类型为 const 在对中,您的 lambda 参数未反映。我建议使用decltype(hits)::value_type const&amp;
  • @Xeo - 如果hits 被声明为typedef - 例如typedef std::map&lt;...&gt; MyMap;hits 被定义为MyMap hits;decltype(hits)::value_type 会成功吗?我发现它在没有typedef 时有效,但在存在时会出现编译器错误。但也许我做错了什么。

标签: c++ c++11 map stl lambda


【解决方案1】:

std::map&lt;K,V&gt;value_typestd::pair&lt;const K, V&gt;。您的 lambda 采用 std::pair&lt;K,V&gt;。注意常量的区别。

转换是通过这个构造函数完成的:

template< class U1, class U2 >
pair( const pair<U1, U2>& p );

(请参阅this reference page 上的 (4))

转换的结果是临时的,临时的可以绑定到 const 引用,所以你的代码可以工作。

【讨论】:

  • 谢谢 - 编译器会将U1 解释为const K 吗?
  • 如果类型错误(在某些情况下)产生错误的一种方法是依赖pair 的分段构造:使用pair&lt; K const&amp;, V const&amp; &gt; 而不是pair&lt;K,V&gt; const&amp;。简而言之,如果你在概念上是tieing 参数,那么就取一个bundle 的引用而不是取一个bundle 的引用。 -- coliru.stacked-crooked.com/a/0855b93de18a442c -- 一个简单的别名 template&lt;typename... Ts&gt; using cref_tie = std::tuple&lt; const Ts&amp;... &gt;; 使这更加简洁。
猜你喜欢
  • 2012-03-05
  • 1970-01-01
  • 2011-12-20
  • 1970-01-01
  • 1970-01-01
  • 2015-12-20
  • 1970-01-01
  • 2011-03-24
  • 2022-01-03
相关资源
最近更新 更多