dimanche 27 septembre 2015

Why Does boost::sort deduce a const range& input type?

When I try to pass the result of a nested boost::accumulate algorithm (where the result is a std::vector) into boost::sort, the compiler deduces that the input of boost::sort is a const std::vector& even though it correctly deduces the return type of boost::accumulate to be std::vector&. Why is that? The code below does not compile, complaining about operator= being undefined for resultT.

#include <boost/range/algorithm/find_if.hpp>
#include <boost/range/algorithm_ext/copy_n.hpp>
#include <boost/range/algorithm/sort.hpp>
#include <boost/range/numeric.hpp>

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

struct resultT
{
    std::string name;
    double quantity;
};

auto operator<(const resultT& lhs, const resultT& rhs) -> bool
{
    return std::tie(lhs.quantity, lhs.name)
        < std::tie(rhs.quantity, rhs.name);
}

auto operator>(const resultT& lhs, const resultT& rhs) -> bool
{
    return rhs < lhs;
}

auto operator<<(std::ostream& os, const resultT& row) -> std::ostream&
{
    os << row.name << '\t' << std::setprecision(4) << std::fixed << row.quantity;
    return os;
}

template<typename T>
auto calculate(const T& in) -> double
{
    //a stand-in for real operations on T--not important to the example
    return in.second;
}

using resultContainer = std::vector<resultT>;

template<typename QuantityT>
auto add(resultContainer& accumulated, const QuantityT& next) -> resultContainer&
{
    auto accumulated_itr{boost::find_if(accumulated, [&next](const resultT& in) -> bool
    {
        return in.name == next.second.first;
    })};

    if (accumulated_itr == std::end(accumulated))
    {
        accumulated.emplace_back(resultT{next.second.first, calculate(next.second)});
    }
    else
    {
        accumulated_itr->quantity += calculate(next.second);
    }

    return accumulated;
}

auto main() -> int
{
    using InnerT = std::pair<int, std::pair<std::string, int>>;
    using OuterT = std::pair<char, std::pair<std::string, int>>;
    auto addInnerOne{[](resultContainer& accumulated, const InnerT& next) { return add<InnerT>(accumulated, next); }};
    auto addOuterOne{[](resultContainer& accumulated, const OuterT& next) { return add<OuterT>(accumulated, next); }};

    auto InnerOne{std::unordered_multimap<int, std::pair<std::string, int>>
    {
        {0, {"hi", 1}}
        , {1, {"ho", 5}}
        , {2, {"hi", 7}}
        , {3, {"ho", 7}}
        , {4, {"hey", 9}}
        , {5, {"fiddle", 11}}
        , {6, {"hey", 11}}
        , {7, {"ho", 3}}
    }};
    auto OuterOne{std::unordered_map<char, std::pair<std::string, int>>
    {
        {'A', {"hi", 1}}
        , {'B', {"ho", 5}}
        , {'C', {"hi", 7}}
        , {'D', {"ho", 7}}
        , {'E', {"hey", 9}}
        , {'F', {"diddle", 21}}
        , {'G', {"hey", 5}}
        , {'H', {"ho", 3}}
    }};

    boost::copy_n(
        boost::sort(
            boost::accumulate(OuterOne
                              , boost::accumulate(InnerOne
                                                  , resultContainer{}
                                                  , addInnerOne)
                              , addOuterOne)
                    , std::greater<resultT>())
        , 5
        , std::ostream_iterator<resultT>(std::cout, "\n"));

    return 0;
}

Here you can see the issue live on Coliru.


Here is a simple fix that goes around the problem. I already have this fix--I want to know why I needed this workaround in the first place:

auto quant{   //quant's type is correctly deduced to be std::vector
    boost::accumulate(OuterOne
                      , boost::accumulate(InnerOne
                                          , resultContainer{}
                                          , addInnerOne)
                      , addOuterOne)};

boost::copy_n(
    boost::sort(quant
                , std::greater<resultT>())
    , 5
    , std::ostream_iterator<resultT>(std::cout, "\n"));

return 0;

Here is the fix live on Coliru.

Aucun commentaire:

Enregistrer un commentaire