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_if
fuera 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 № 1Aquí 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{};
}