0

My problem is to test an implementation of quicksort with different comparator functions: std::less and std::greater. But I do not want to copy-paste testing code that differs just in two comparators, so I would like to put them into a vector (or maybe something else?) and iterate over them.

To simplify this post, lets say I would like to write a loop over a vector of two functions that get 0 and 1 as their arguments and output a boolean. Here is my take at that:

#include <iostream>
#include <vector>
#include <functional>

int main() {
    auto fs = std::vector<>{std::less<int>{}, std::greater<int>{}};

    for (auto f: fs) {
        std::cout << f(0, 1) << " ";
    } std::cout << std::endl;
}

My g++ 6.1.1 compiler rightfully complains that I have not specified the template arguments for the vector. I have been trying things like std::function<bool(int, int)> and others with no luck.

Could you tell me how to fix this piece of code?


Update: The exact error I am getting:

% g++ -std=c++14 -Wall deleteme.cpp && ./a.out
deleteme.cpp: In function ‘int main()’:
deleteme.cpp:6:27: error: wrong number of template arguments (0, should be at least 1)
     auto fs = std::vector<>{std::less<int>{}, std::greater<int>{}};
                           ^
In file included from /usr/include/c++/6.1.1/vector:64:0,
                 from deleteme.cpp:2:
/usr/include/c++/6.1.1/bits/stl_vector.h:214:11: note: provided for ‘template<class _Tp, class _Alloc> class std::vector’
     class vector : protected _Vector_base<_Tp, _Alloc>
           ^~~~~~
deleteme.cpp:8:18: error: unable to deduce ‘auto&&’ from ‘fs’
     for (auto f: fs) {
                  ^~
ildjarn
  • 62,044
  • 9
  • 127
  • 211
alisianoi
  • 2,003
  • 3
  • 31
  • 46

2 Answers2

2

Only from C++17, template arguments of the constructor can be used to deduce the template arguments of the type, so you will have to write std::vector<std::function<bool(int,int)>> instead of std::vector<>.

Please note that std::function has a performance overhead compared to calling functions directly, so you might want to check out variadic template arguments (and swallowing) for getting the last few percentages

Community
  • 1
  • 1
JVApen
  • 11,008
  • 5
  • 31
  • 67
  • Thank you for your reply. I have added `std::function` back into vector and it worked. I could swear it did not work 30 mins ago. – alisianoi Aug 21 '16 at 11:30
  • Could you please elaborate on your second paragraph about `std::greater`, `std::greater{}` and `std::greater()`? For me, the first is just a type, the second and the third are calling default constructors for the `struct std::greater` and do not differ. Do they differ? – alisianoi Aug 21 '16 at 11:32
  • Sorry, my bad, that is wrong. I'm used to work with a codebase which has similar concepts as the STL and sometimes mix them up. std::greater is indeed a struct, not a free function. – JVApen Aug 21 '16 at 11:33
0

As you use different types, you might use tuple, and iterate over tuple:

namespace detail {
    template <typename F, typename TUPLE, std::size_t ... Is>
    void run_for_each(F&& f, TUPLE&& t, std::index_sequence<Is...>)
    {
        const int dummy[] = {0, (f(std::get<Is>(std::forward<TUPLE>(t))), void(), 0)...};
        static_cast<void>(dummy); // Avoid warning for unused variable
    }
}

template <typename F, typename TUPLE>
void run_for_each(F&& f, TUPLE&& t)
{
     detail::run_for_each(std::forward<F>(f),
                          std::forward<TUPLE>(t),
                          std::make_index_sequence<
                              std::tuple_size<std::decay_t<TUPLE>>::value>());
}

And then

int main() {
    auto t = std::make_tuple(std::less<int>{}, std::greater<int>{});

    run_for_each([](auto&&f) {std::cout << f(0, 1) << " ";}, t);
    std::cout << std::endl;
}

Demo

Jarod42
  • 203,559
  • 14
  • 181
  • 302