A more elegant way for a template function grouping values?

204 views Asked by At

The following code groups values for any container with a generic grouping lambda:

template<class Iterator, class GroupingFunc,
         class T = remove_reference_t<decltype(*declval<Iterator>())>,
         class GroupingType = decltype(declval<GroupingFunc>()(declval<T&>()))>
auto groupValues(Iterator begin, Iterator end, GroupingFunc groupingFunc) {
    map<GroupingType, list<T>> groups;
    for_each(begin, end,
        [&groups, groupingFunc](const auto& val){
            groups[groupingFunc(val)].push_back(val);
    } );
    return groups;
}

With the following usage:

int main() {
    list<string> strs = {"hello", "world", "Hello", "World"};
    auto groupOfStrings =
        groupValues(strs.begin(), strs.end(), 
            [](auto& val) {
                return (char)toupper(val.at(0));
        });
    print(groupOfStrings); // assume a print method

    list<int> numbers = {1, 5, 10, 24, 13};
    auto groupOfNumbers =
        groupValues(numbers.begin(), numbers.end(), 
            [](int val) {
                int decile = int(val / 10) * 10;
                return to_string(decile) + '-' + to_string(decile + 9);
        });
    print(groupOfNumbers); // assume a print method
}

I am a bit reluctant regarding the (over?)-use of declval and decltype in groupValues function.

Do you see a better way for writing it?

(Question is mainly for better style and clarity unless of course you see any other issue).


Code: http://coliru.stacked-crooked.com/a/f65d4939b402a750

1

There are 1 answers

1
Biggy Smith On BEST ANSWER

I would probably move the last two template parameters inside the function, and use std::result_of to give a slightly more tidy function:

template <typename T>
using deref_iter_t = std::remove_reference_t<decltype(*std::declval<T>())>;

template<class Iterator, class GroupingFunc>
auto groupValues(Iterator begin, Iterator end, GroupingFunc groupingFunc) {
    using T = deref_iter_t<Iterator>;
    using GroupingType = std::result_of_t<GroupingFunc(T&)>;
    std::map<GroupingType, std::list<T>> groups;
    std::for_each(begin, end, [&groups, groupingFunc](const auto& val){
        groups[groupingFunc(val)].push_back(val);
    });
    return groups;
}

live demo