【问题标题】:boost::adaptor::filtered core dumps with boost::range_detail::default_constructible_unary_fn_wrapper "Assertion `m_impl' failed"boost::adaptor::filtered core dumps with boost::range_detail::default_constructible_unary_fn_wrapper "Assertion `m_impl' failed"
【发布时间】:2016-07-09 21:46:17
【问题描述】:

当我运行此代码时,boost::range_detail::default_constructible_unary_fn_wrapper 内部出现断言失败。断言似乎是在检查函子是否已在过滤器对象中初始化。

#include <boost/range/adaptor/filtered.hpp>

#include <iomanip>
#include <iostream>
#include <map>
#include <unordered_map>
#include <vector>

template<class RangeType>
static auto get_range_and_range_size(const RangeType& in_range, std::input_iterator_tag) -> auto {
    /*
     * we have an InputRange, but we need to know the size of the range.
     * so we eagerly copy the values into a vector so we can get the size of the range
     * and also return the range.
     * */
    auto out_range = std::vector<decltype(*std::begin(in_range))>(
        std::begin(in_range),
        std::end(in_range)
    );

    return std::make_tuple(std::move(out_range), out_range.size());
}

template<class RangeType>
static auto get_range_and_range_size(const RangeType& in_range, std::forward_iterator_tag) -> auto {
    return std::make_tuple(std::ref(in_range), boost::distance(in_range));
}

struct Cache {
    std::unordered_map<int, int> m_map;

    template<class RangeT>
    auto Insert(RangeT in_range) -> void {
        typename std::iterator_traits<
            decltype(std::begin(in_range))
        >::iterator_category iterator_category;

        std::cout << "No core dump yet!\n";

        auto range_and_range_size = get_range_and_range_size(
            boost::adaptors::filter(
                in_range,
                /*
                 * filter out any keys that are already in the cache and NOT TTLed yet
                 * */
                [this](const auto& key_value) -> bool {
                    const auto find_itr = m_map.find(key_value.first);

                    return (m_map.end() == find_itr)    /*  the key was not in the cache    */
                           || hasTTLed(find_itr->second);  /*  the key was in the cache but its value has TTLed  */
                }
            ),
            iterator_category
        );

        for(auto&& key_value : std::get<0>(range_and_range_size)) {
            m_map.emplace(key_value);
        }

        std::cout << "Can't reach this code. :(\n";
    }

    auto hasTTLed(int) const noexcept -> bool {
        /*
         *  TTL impl goes here
         */
        return false;
    }
};

auto main(int, char*[]) -> int {
    Cache a{};

    std::vector<std::pair<int, int>> v{
        {0, 0},
        {1, 1},
        {2, 2},
        {3, 3}
    };

    a.Insert(v);

    std::map<int, int> b{
        {0, 1},
        {1, 0},
        {2, 1},
        {3, 1}
    };

    a.Insert(b);
}

我可以通过 coliru 使用的任何版本的 boost 以及 1.60 得到这个。

转载于大肠杆菌here

你能帮我理解a)这里发生了什么,b)我如何解决它?

【问题讨论】:

  • 从断言失败的名称来看,我的猜测是filter 适配器想要一个默认的可构造谓词,而 lambda 不是默认可构造的。
  • 不过,它并没有在文档中的任何地方命名该要求。
  • (这里挑剔,但如果 lambda 没有捕获,它可能是默认可构造的)
  • thisfilter_iterator 的文档确实提到谓词需要是默认可构造的。

标签: c++ boost c++14 boost-range


【解决方案1】:

这是一生的问题。

问题出在get_range_and_range_size,它正在捕获const RangeType&amp;,而它本应捕获RangeType&amp;&amp;。将std::ref 返回到以这种方式捕获的临时对象会导致对作用于range_and_range_size 的代码中的未初始化范围进行调用。

get_range_and_range_size 的这些定义解决了这个问题。

template<class RangeType>
static auto get_range_and_range_size(RangeType&& in_range, std::input_iterator_tag) -> auto {
    /*
     * we have an InputRange, but we need to know the size of the range.
     * so we eagerly copy the values into a vector so we can get the size of the range
     * and also return the range.
     * */
    auto out_range = std::vector<decltype(*std::begin(in_range))>(
        std::begin(in_range),
        std::end(in_range)
    );

    const auto out_size = out_range.size(); /*  capture the value before moving the container   */
    return std::make_tuple(std::move(out_range), out_size);
}

template<class RangeType>
static auto get_range_and_range_size(RangeType&& in_range, std::forward_iterator_tag) -> auto {
    const auto size = boost::distance(in_range); /*  capture the value before moving the container   */
    return std::make_tuple(std::move(in_range), size);
}

【讨论】:

  • 在第二次重载中调用make_tuple 时应该std::forward&lt;RangeType&gt;(in_range)。我在之前的评论中关于必须单独获取size() 是错误的。 make_tuple 完美转发它的参数,所以你调用它时只是绑定引用,实际上没有移动构造发生,所以在第二个参数中调用 .size() 就可以了。
  • 谢谢。我假设在input_iterator_tag 函数的情况下,我仍然需要std::move 向量,因为它不是临时的。
猜你喜欢
  • 1970-01-01
  • 2011-05-31
  • 2020-01-19
  • 2021-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-01
相关资源
最近更新 更多