Как я могу использовать Boost.Hana, чтобы определить, есть ли у функтора оператор вызова, который может быть вызван с определенным аргументом шаблона?

В моем приложении я хочу во время компиляции определить, является ли произвольный тип функтора Func имеет нулевой оператор вызова, который может быть вызван с заданным явным аргументом шаблона T, Основываясь на предыдущем ответе SO, который я нашел, я придумал следующее:

#include <boost/hana.hpp>
#include <iostream>
#include <type_traits>

namespace hana = boost::hana;

namespace detail 
{
    template <typename T>
    auto can_call = hana::is_valid([](auto &&f) -> 
        decltype(f.template operator()<T>()) { });
}

template <typename Func, typename T>
constexpr auto can_call() ->
    decltype(detail::can_call<typename std::remove_reference<T>::type>(
        std::declval<Func>())) { return {}; }

struct foo
{
    template <typename T, typename = 
        std::enable_if_t<!std::is_same<T, char>::value>>
    void operator()() const { }
};

int main()
{
    std::cout << "char: " << can_call<foo, char>() << std::endl;
    std::cout << "int: " << can_call<foo, int>() << std::endl;
}

Я ожидаю, что этот пример распечатает:

char: 0
int: 1

Так как char Тип аргумента шаблона явно enable_ifв foo, Я пробовал следующие компиляторы:

  • Apple clang v8.0.0: пример компилируется и работает, как и ожидалось.
  • mainline clang v3.9.1 + (через Wandbox): пример компилируется и запускается, как и ожидалось.
  • mainline clang v3.6.0 - v3.8.1 (через Wandbox): компилятор умирает с внутренней ошибкой.
  • магистраль g++ 7.0, 20170410 (через Wandbox): компиляция завершается со следующими ошибками:

    dd.cc: In instantiation of ‘auto detail::can_call<char>’:
    dd.cc:15:14:   required by substitution of ‘template<class Func, class T> constexpr decltype (can_call<typename std::remove_reference<_To>::type>(declval<Func>())) can_call() [with Func = foo; T = char]’
    dd.cc:25:50:   required from here
    dd.cc:10:10: error: ‘auto detail::can_call<char>’ has incomplete type
        auto can_call = hana::is_valid([](auto &&f) -> decltype(f.template operator()<T>()) { });
            ^~~~~~~~
    dd.cc: In function ‘int main()’:
    dd.cc:25:50: error: no matching function for call to ‘can_call<foo, char>()’
        std::cout << "char: " << can_call<foo, char>() << std::endl;
                                                    ^
    dd.cc:14:16: note: candidate: template<class Func, class T> constexpr decltype (can_call<typename std::remove_reference<_To>::type>(declval<Func>())) can_call()
    constexpr auto can_call() ->
                ^~~~~~~~
    dd.cc:14:16: note:   substitution of deduced template arguments resulted in errors seen above
    dd.cc: In instantiation of ‘auto detail::can_call<int>’:
    dd.cc:15:14:   required by substitution of ‘template<class Func, class T> constexpr decltype (can_call<typename std::remove_reference<_To>::type>(declval<Func>())) can_call() [with Func = foo; T = int]’
    dd.cc:26:48:   required from here
    dd.cc:10:10: error: ‘auto detail::can_call<int>’ has incomplete type
        auto can_call = hana::is_valid([](auto &&f) -> decltype(f.template operator()<T>()) { });
            ^~~~~~~~
    dd.cc:26:48: error: no matching function for call to ‘can_call<foo, int>()’
        std::cout << "int: " << can_call<foo, int>() << std::endl;
                                                ^
    dd.cc:14:16: note: candidate: template<class Func, class T> constexpr decltype (can_call<typename std::remove_reference<_To>::type>(declval<Func>())) can_call()
    constexpr auto can_call() ->
                ^~~~~~~~
    dd.cc:14:16: note:   substitution of deduced template arguments resulted in errors seen above
    

Кажется, не нравится мое использование hana::is_valid() определить, существует ли указанный оператор. Тем не менее, я думаю, что способ, которым я его использую, соответствует его предполагаемому использованию.

Это ошибка в gcc, более мягкая реализация в современных версиях clang, или я неправильно реализовал этот тип проверки? Кажется, что это определенно в рулевой рубке Ханы; Я просто пытаюсь обернуть голову вокруг своей новой модели constexpr метапрограммированием.

1 ответ

Решение

Вот обходной путь, который использует структуру "функтор" вместо лямбды и дополнительный уровень косвенности для типа is_valid экземпляр, чтобы успокоить GCC.

namespace detail 
{
    template <typename T>
    struct check_can_call { 
      template <typename F>
      constexpr auto operator()(F&& f) -> 
        decltype(f.template operator()<T>()) { }
    };

    template <typename T>
    using is_call_valid = decltype(hana::is_valid(check_can_call<T>{}));

    template <typename T>
    constexpr is_call_valid<T> can_call{};
}
Другие вопросы по тегам