/ / ¿Cómo puedo usar Boost.Hana para determinar si un functor tiene un operador de llamada que puede invocarse con un argumento de plantilla en particular? - c ++ 14, boost-hana

¿Cómo puedo usar Boost.Hana para determinar si un funtor tiene un operador de llamada que se puede invocar con un argumento de plantilla particular? - c ++ 14, boost-hana

En mi aplicación, quiero determinar en tiempo de compilación si un tipo de functor arbitrario Func tiene un operador de llamada nula que se puede invocar con un argumento de plantilla explícito dado T. Basado en una respuesta SO anterior que encontré, Se me ocurrió lo siguiente:

#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;
}

Espero que este ejemplo se imprima:

char: 0
int: 1

Desde el char tipo de argumento de la plantilla es explícitamente enable_iffuera en foo. He probado los siguientes compiladores:

  • Apple clang v8.0.0: El ejemplo compila y se ejecuta como se esperaba.
  • mainline clang v3.9.1 + (a través de Wandbox): el ejemplo se compila y se ejecuta como se esperaba.
  • mainline clang v3.6.0 - v3.8.1 (a través de Wandbox): el compilador muere con un error interno.
  • Tronco g ++ 7.0, 20170410 (a través de Wandbox): La compilación falla con los siguientes errores:

    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
    

Parece que no le gusta mi uso de hana::is_valid() para determinar si el operador especificado existe. Sin embargo, creo que la forma en que lo estoy usando es consistente con su uso previsto.

¿Es este un bug en gcc, un poco más indulgente?¿Implementación en versiones de clang contemporáneo, o implementé este tipo de verificación incorrectamente? Parece que esto definitivamente está dentro de la caseta de Hana; estoy tratando de envolver mi cabeza en torno a su nuevo modelo de constexpr metaprogramación.

Respuestas

2 para la respuesta № 1

Aquí hay una solución que utiliza un struct "functor" en lugar de un lambda y una capa adicional de direccionamiento indirecto para el tipo de is_valid instancia para apaciguar a 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{};
}