Ошибка вывода перегруженных шаблонных функций - PullRequest
0 голосов
/ 01 ноября 2018

Я изо всех сил пытаюсь понять это правильно. Я хочу создать перегруженные шаблоны функций ада, которые бы делали такие вызовы возможными и правильными (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);

Все хорошо, кроме этого утверждения:

ASSERT_EQ(min(std::less<>(), 3,2,1), 2);

И когда я извлекаю саму функцию, чтобы получить значимую ошибку:

min(std::less<>(), 3,2,1)

Я получаю это:

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

Функции шаблона приведены ниже:

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...));
    }
}

Ошибки указывают на функцию # 2, хотя она должна использовать # 4.

1 Ответ

0 голосов
/ 01 ноября 2018

Я полагаю, ошибка в следующей функции шаблона: вы должны добавить что-то как * = nullptr после последнего ::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...));
}

или также добавьте typename = перед последним typename std::enable_if.

В противном случае, если все идет хорошо (если Firts и все Args... равны и если Comparable конвертируется в необходимый st::function, подпись шаблона становится

template <typename Comparator, typename First, typename ... Args,
          typename = std::enable_if<are_same<First, Args...>::value,
          void>

и последний void, сам по себе, не имеет смысла (а предыдущий std::enable_if не очень полезен; но это еще одна проблема; см. Следующее «предложение бонуса»)

Вы должны превратиться во что-то похожее на

template <typename Comparator, typename First, typename ... Args,
          typename = std::enable_if<are_same<First, Args...>::value, void>
          void * = nullptr>
//............^^^^^^^^^^^^

или также

template <typename Comparator, typename First, typename ... Args,
          typename = std::enable_if<are_same<First, Args...>::value, void>,
          typename = void>
//........^^^^^^^^^^^

Бонусное предложение: предыдущий тест SFINAE должен быть

typename = std::enable_if_t<are_same<First, Args...>::value, void>
// ......................^^

или также (последний параметр std::enable_if_t по умолчанию void)

typename = std::enable_if_t<are_same<First, Args...>::value>

в противном случае тест никогда не срабатывает, а функция когда-либо включена (с точки зрения типов First и Args...).

Аналогичная проблема в тесте SFINAE для

First min(First first, Args... args) 
...