I struggle to get this right. I want to create overloaded template functions hell that would make such calls possible and correct (GMock):
ASSERT_EQ(min(1, 2), 1);
ASSERT_EQ(min(std::less<>(),3,2), 2);
auto abs_comp = [](auto el1, auto el2){
return std::abs(el1) < std::abs(el2);
};
ASSERT_EQ(min(abs_comp, -1, -5), -1);
ASSERT_EQ(min(4, 3, 2, 1), 1);
All is good except for this assertion:
ASSERT_EQ(min(std::less<>(), 3,2,1), 2);
And when I extract the function itself to get a meaningful error:
min(std::less<>(), 3,2,1)
I get this:
In file included from /home/rumcajs/CLionProjects/ModernCppChallenge/tst/lang/container_minimum_test.cpp:4:
/home/rumcajs/CLionProjects/ModernCppChallenge/tst/lang/../../src/lang/container_minimum.h: In instantiation of ‘First cppchallenge::lang::min(First, Args ...) [with First = int; Args = {}; <template-parameter-1-3> = std::enable_if<true, void>]’:
/home/rumcajs/CLionProjects/ModernCppChallenge/tst/lang/../../src/lang/container_minimum.h:17:30: required from ‘First cppchallenge::lang::min(First, Args ...) [with First = std::less<void>; Args = {int}; <template-parameter-1-3> = std::enable_if<false, void>]’
/home/rumcajs/CLionProjects/ModernCppChallenge/tst/lang/../../src/lang/container_minimum.h:17:19: required from ‘First cppchallenge::lang::min(First, Args ...) [with First = std::less<void>; Args = {int, int, int}; <template-parameter-1-3> = std::enable_if<false, void>]’
/home/rumcajs/CLionProjects/ModernCppChallenge/tst/lang/container_minimum_test.cpp:40:33: required from here
/home/rumcajs/CLionProjects/ModernCppChallenge/tst/lang/../../src/lang/container_minimum.h:17:30: error: no matching function for call to ‘min()’
return min(first, min(args...));
~~~^~~~~~~~~
/home/rumcajs/CLionProjects/ModernCppChallenge/tst/lang/../../src/lang/container_minimum.h:8:7: note: candidate: ‘template<class T> T cppchallenge::lang::min(T, T)’
T min(T first, T second) {
^~~
/home/rumcajs/CLionProjects/ModernCppChallenge/tst/lang/../../src/lang/container_minimum.h:8:7: note: template argument deduction/substitution failed:
/home/rumcajs/CLionProjects/ModernCppChallenge/tst/lang/../../src/lang/container_minimum.h:17:30: note: candidate expects 2 arguments, 0 provided
return min(first, min(args...));
~~~^~~~~~~~~
/home/rumcajs/CLionProjects/ModernCppChallenge/tst/lang/../../src/lang/container_minimum.h:16:11: note: candidate: ‘template<class First, class ... Args, class> First cppchallenge::lang::min(First, Args ...)’
First min(First first, Args... args) {
^~~
/home/rumcajs/CLionProjects/ModernCppChallenge/tst/lang/../../src/lang/container_minimum.h:16:11: note: template argument deduction/substitution failed:
/home/rumcajs/CLionProjects/ModernCppChallenge/tst/lang/../../src/lang/container_minimum.h:17:30: note: candidate expects at least 1 argument, 0 provided
return min(first, min(args...));
~~~^~~~~~~~~
gmake[3]: *** [CMakeFiles/ModernCppChallengeLang.dir/build.make:102: CMakeFiles/ModernCppChallengeLang.dir/tst/lang/container_minimum_test.cpp.o] Error 1
gmake[2]: *** [CMakeFiles/Makefile2:116: CMakeFiles/ModernCppChallengeLang.dir/all] Error 2
gmake[1]: *** [CMakeFiles/Makefile2:128: CMakeFiles/ModernCppChallengeLang.dir/rule] Error 2
gmake: *** [Makefile:177: ModernCppChallengeLang] Error 2
The template functions are as below:
namespace cppchallenge::lang {
//#1
template<typename T>
T min(T first, T second) {
return first < second ? first : second;
}
template<typename First, typename... Args>
using are_same = std::conjunction<std::is_same<First, Args>...>;
//#2
template<typename First, typename... Args, typename = std::enable_if<are_same<First, Args...>::value, void>>
First min(First first, Args... args) {
return min(first, min(args...));
}
//#3
template<typename Comparator, typename T>
T min(Comparator comp, T first, T second) {
return comp(first, second) ? first : second;
}
//#4
template<typename Comparator, typename First, typename... Args,
typename = std::enable_if<are_same<First, Args...>::value, void>,
typename std::enable_if<std::is_convertible<Comparator, std::function<bool(First,First)>>::value>::type>
First min(Comparator comp, First first, Args... args) {
return min(comp, first, min(comp, args...));
}
}
The errors are pointing to function #2 though it should use #4.
I suppose the error is in the following template function: you have to add something as * = nullptr
after the last ::type
template<typename Comparator, typename First, typename... Args,
typename = std::enable_if<are_same<First, Args...>::value, void>,
typename std::enable_if<std::is_convertible<Comparator, std::function<bool(First,First)>>::value>::type * = nullptr> // add * = nullptr
First min(Comparator comp, First first, Args... args) {
return comp(comp, first, min(comp, args...));
}
or also add a typename =
before the last typename std::enable_if
.
Otherwise, if all goes well (if Firts
and all Args...
are equal and if Comparable
is convertible to the needed st::function
, the template signature become
template <typename Comparator, typename First, typename ... Args,
typename = std::enable_if<are_same<First, Args...>::value,
void>
and the last void
, alone, doesn't make sense (and the preceding std::enable_if
isn't much useful; but this is another problem; see the following "bonus suggestion")
You should transform in something similar to
template <typename Comparator, typename First, typename ... Args,
typename = std::enable_if<are_same<First, Args...>::value, void>
void * = nullptr>
//............^^^^^^^^^^^^
or also
template <typename Comparator, typename First, typename ... Args,
typename = std::enable_if<are_same<First, Args...>::value, void>,
typename = void>
//........^^^^^^^^^^^
Bonus suggestion: the preceding SFINAE test should be
typename = std::enable_if_t<are_same<First, Args...>::value, void>
// ......................^^
or also (the last std::enable_if_t
parameter is void
by default)
typename = std::enable_if_t<are_same<First, Args...>::value>
otherwise the test never works and the function is ever enabled (from the point of view of the First
and Args...
types).
Similar problem in the SFINAE test for
First min(First first, Args... args)