Обобщение is_detected с параметрами переменной функции - PullRequest
1 голос
/ 07 апреля 2019

Я пытаюсь изменить идиому is_detected, чтобы разрешить передачу ей переменных аргументов.Мне это нужно, так как некоторые из моих обнаруженных функций-членов будут иметь предоставленные пользователем аргументы.

Пока что это то, что я получил, работая.Вы присваиваете дополнительные аргументы is_detected_args_v, и теоретически специализация шаблона включается и компилируется правильно.Таким образом, давая std::true_type.

#include <type_traits>
#include <cstdio>

// slightly modified (and simplified) is_detected
template <template <class, class...> class Op, class T, class = void, class...>
struct is_detected_args : std::false_type {};
template <template <class, class...> class Op, class T, class... Args>
struct is_detected_args<Op, T, std::void_t<Op<T, Args...>>, Args...>
        : std::true_type {};

template <template <class, class...> class Op, class T, class... Args>
inline constexpr bool is_detected_args_v
        = is_detected_args<Op, T, Args...>::value;

// has_func, checks the function starts with int, and then Args&...
template <class T, class... Args>
using has_func = decltype(std::declval<T>().func(
        std::declval<int>(), std::declval<Args&>()...));


// has the func
struct obj {
    void func(int, double&, double&) {
        printf("potato\n");
    }
};

int main(int, char**) {
    obj o;

    if constexpr(is_detected_args_v<has_func, obj, double, double>) {
        double d = 0;
        double d2 = 42;
        o.func(42, d, d2);
    }
}

. Вы можете запустить пример здесь (протестировано на всех 3-х компиляторах): https://wandbox.org/permlink/ttCmWSVl1XVZjty7

Проблема в том, что специализация никогда не выбирается, а условнаявсегда ложно.У меня вопрос в два раза.

  1. Возможно ли это вообще?
  2. Почему is_detected не специализируется?

Thx

1 Ответ

1 голос
/ 07 апреля 2019

Основной проблемой здесь является неправильное понимание того, что делает void_t.Для повышения квалификации см. , как работает void_t? .Ключевая идея заключается в том, что основной шаблон имеет параметр void, а специализация имеет некоторую сложную вещь, которую вы хотите проверить, заключенную в void_t, чтобы он соответствовал параметру основного шаблона.Этого не происходит в вашем примере.

Мы можем исправить это в два простых шага.Во-первых, у вас есть этот тип T вместе с Args... На самом деле нет никакой причины разделять это, и на это легче смотреть, если у нас нет посторонних параметров.Итак, вот ваша попытка только что уменьшена (я также дал имя параметру, который должен быть void):

template <template <class...> class Op, class AlwaysVoid, class...>
struct is_detected_args : std::false_type {};
template <template <class...> class Op, class... Args>
struct is_detected_args<Op, std::void_t<Op<Args...>>, Args...>
        : std::true_type {};

template <template <class...> class Op, class... Args>
inline constexpr bool is_detected_args_v = is_detected_args<Op, Args...>::value;

Теперь должно быть проще увидеть, чего не хватает: параметр void!Вы не проходите в void, и вам нужно.Это легко исправить:

template <template <class...> class Op, class... Args>
inline constexpr bool is_detected_args_v = is_detected_args<Op, void, Args...>::value;
//                                                              ~~~~~

И теперь все работает как положено.

Cppreference также предоставляет полную реализацию is_detected, если вы хотите посмотреть на это тоже.

...