Использовать invoke_result с типом аргумента void? - PullRequest
0 голосов
/ 07 января 2019

Я пытаюсь сделать следующее:

struct Unwrapper
{
    template<typename T>
    auto operator()(const T& arg, std::enable_if_t<isPrimitive<T>, void>* = nullptr) {return arg;}

    template<typename T>
    auto operator()(const T& arg, std::enable_if_t<!isPrimitive<T>, void>* = nullptr) {return arg.wrapped();}

    void operator()(void) {}
};

template<typename T>
using UnwrappedT = std::invoke_result_t<Unwrapper, T>; // error: no type named ‘type’ in ‘struct std::invoke_result<Unwrapper, void>’

Документы для std :: invoke_result предполагают, что он должен работать для Args, являющегося void (то есть ни одного), в частности, он говорит, что неработающий случай void был "причудой" ныне устаревшей std::result_of.

Но нет, void не работает. Это имеет смысл, потому что нельзя также сделать std::declval<T>() для T = void, а std::invoke_result предполагается реализовать в терминах std::declval.

Вопрос в том, какой самый элегантный / прямой способ исправления кода для работы с void? Я мог бы что-то сделать с std::conditional, но я ожидал лучшего. (с использованием C ++ 17)

Соответствующий вопрос: это , это .

1 Ответ

0 голосов
/ 07 января 2019

Вы можете сделать это:

template<typename... T>
using UnwrappedT = std::invoke_result_t<Unwrapper, T...>; 

UnwrappedT<> будет обрабатывать случай void.

Если вы хотите, чтобы UnwrappedT<void> означало UnwrappedT<>, вам понадобится какой-то способ отбросить void. conditional - самый знакомый способ сделать это:

template<typename T>
using UnwrappedT = typename std::conditional_t<
    std::is_void_v<T>,
    std::invoke_result<Unwrapper>,
    std::invoke_result<Unwrapper, T>>::type;

Или вы можете повеселиться с Boost.Mp11:

template<typename T>
using UnwrappedT = mp_apply<std::invoke_result_t,
    mp_remove_if<mp_list<Unwrapper, T>, std::is_void>>;
...