2016-03-22 3 views
3

У меня возникает ошибка утверждения внутри boost::range_detail::default_constructible_unary_fn_wrapper при запуске этого кода. Утверждение, похоже, проверяет, что функтор инициализирован внутри объекта фильтра.boost :: adapter :: filter core dumps с boost :: range_detail :: default_constructible_unary_fn_wrapper "Assertion` m_impl 'failed "

#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); 
} 

Я получаю это с любой версией boost, которую использует coliru, а также 1.60.

Воспроизведено на coliru here.

Можете ли вы помочь мне понять, что), что здесь происходит, и б) как я это исправим?

+0

От имени ошибки утверждения моя догадка заключается в том, что адаптер 'filter' требует конструктивного предиката по умолчанию, а lambda не является конструктивным по умолчанию. – Praetorian

+0

Однако это не упоминает это требование в документации. – caps

+0

(nitpicking здесь, но лямбда может быть по умолчанию конструктивна, если у нее не было захватов) – caps

ответ

2

Это был вопрос о жизни.

Вопрос был в get_range_and_range_size, который захватывал const RangeType&, когда он должен был захватить RangeType&&. Возврат 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); 
} 
+0

Вы должны 'std :: forward (in_range)' при вызове 'make_tuple' во второй перегрузке. И я ошибался в своем предыдущем комментарии о том, что нужно будет использовать 'size()' отдельно. 'make_tuple' идеально пересылает свои аргументы, поэтому вы ссылаетесь только на ссылки, когда вы его называете, конструкция перемещения фактически не имеет места, поэтому вызов' .size() 'во втором аргументе прекрасен. – Praetorian

+0

Спасибо. Я предполагаю, что в случае функции 'input_iterator_tag' мне все равно нужно« std :: move »вектор, так как он не является временным. – caps

+0

Да, абсолютно. – Praetorian

Смежные вопросы