Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Define a function in function declaration using std::iterator traits and auto

Tags:

c++

c++11

Today I tried to implement radix sort. The function must have two variables: begin iterator and end iterator, and can have third: some function that must return the integer type to sort by. By default it must be the identity function.

My tries look like (sorry, code looks very long and dirty, but it's just a try):

template<class ForwardIt>
void radix_sort(
    ForwardIt first,
    ForwardIt last,
    std::function<auto(typename std::iterator_traits<ForwardIt>::value_type)> get_value =
    [](const typename std::iterator_traits<ForwardIt>::value_type& x){ return x; }) {
        // ...
}

The returning type of get_value, of course, will be known in compilation time.

Usage should be:

std::vector<std::pair<uint32_t, std::string>> vec;
// ...
radix_sort(vec.begin(), vec.end(), [](const std::pair<uint32_t, std::string>& x){ return x.first; })

Or:

std::vector<uint32_t> vec;
// ...
radix_sort(vec.begin(), vec.end());

It doesn't even compile and I don't know how to solve the problem. How to do it? Simple example:

#include <bits/stdc++.h>

template<class ForwardIt>
void radix_sort(
    ForwardIt first,
    ForwardIt last,
    std::function<auto(typename std::iterator_traits<ForwardIt>::value_type)> get_value =
    [](const typename std::iterator_traits<ForwardIt>::value_type& x){ return x; }) {
        // ...
}

int main()
{
    std::vector<std::pair<uint32_t, std::string>> vec(10);
    radix_sort(vec.begin(), vec.end());
}

Compiler output:

source_file.cpp:17:37: error: no matching function for call to ‘radix_sort(std::vector<unsigned int>::iterator, std::vector<unsigned int>::iterator)’
     radix_sort(vec.begin(), vec.end());
                                      ^
source_file.cpp:6:6: note: candidate: template<class ForwardIt, class auto:1> void radix_sort(ForwardIt, ForwardIt, std::function<auto:1(typename std::iterator_traits<_Iter>::value_type)>)
 void radix_sort(
      ^
source_file.cpp:6:6: note:   template argument deduction/substitution failed:
source_file.cpp:17:37: note:   couldn't deduce template parameter ‘auto:1’
     radix_sort(vec.begin(), vec.end());
like image 978
Alexander Stanovoy Avatar asked May 13 '19 12:05

Alexander Stanovoy


2 Answers

The easy way to fix this is to not have a default function and instead have two overloads. This lets you get rid of using a std::function, which is expensive, at the cost of writing a couple lines of boiler plate code. If you use

template<class ForwardIt, class Func>
void radix_sort(ForwardIt first, ForwardIt last, Func get_value) {
    // ...
}

template<class ForwardIt>
void radix_sort(ForwardIt first, ForwardIt last) {
    radix_sort(first, last, [](const typename std::iterator_traits<ForwardIt>::value_type& x){ return x; });
}

the you get the default "identity" with no function, and you get the exact function object otherwise if one is provided.

like image 87
NathanOliver Avatar answered Nov 11 '22 08:11

NathanOliver


For the benefit of future users, I would like to point out that C++20 introduces the class std::identity, which aids in solving the problem. With its help, the code can be rewritten as:

template <typename For, typename F = std::identity>
void radix_sort(For first, For end, F f = {})
{
  /* ... */
}

And it is very easy to implement a standard-conforming one yourself if you don't have C++20, like this:

struct identity {
  template <class T>
  constexpr T&& operator()(T&& t) const noexcept
  {
    return std::forward<T>(t);
  }

  using is_transparent = void;
};
like image 25
L. F. Avatar answered Nov 11 '22 08:11

L. F.