/ / Wie kann ich Boost.Hana verwenden, um festzustellen, ob ein Funktor über einen Aufrufoperator verfügt, der mit einem bestimmten Vorlagenargument aufgerufen werden kann? - C ++ 14, Boost-Hana

Wie kann ich Boost.Hana verwenden, um festzustellen, ob ein Funktor über einen Aufrufoperator verfügt, der mit einem bestimmten Vorlagenargument aufgerufen werden kann? - C ++ 14, Boost-Hana

In meiner Anwendung möchte ich zur Kompilierzeit feststellen, ob ein beliebiger Funktortyp vorliegt Func hat einen reinen Aufrufoperator, der mit einem gegebenen expliziten Template-Argument aufgerufen werden kann T. Basierend auf einer früheren SO-Antwort, die ich gefunden habeIch habe folgendes gefunden:

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

Ich würde erwarten, dass dieses Beispiel ausgedruckt wird:

char: 0
int: 1

Seit der char Template-Argumenttyp ist explizit enable_ifin foo. Ich habe folgende Compiler ausprobiert:

  • Apple clang v8.0.0: Das Beispiel kompiliert und läuft wie erwartet.
  • mainline clang v3.9.1 + (via Wandbox): Das Beispiel kompiliert und läuft wie erwartet.
  • mainline clang v3.6.0 - v3.8.1 (via Wandbox): Der Compiler stirbt mit einem internen Fehler ab.
  • g ++ 7.0 trunk, 20170410 (via Wandbox): Die Übersetzung schlägt mit folgenden Fehlern fehl:

    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
    

Es scheint nicht so zu sein wie ich hana::is_valid() um festzustellen, ob der angegebene Operator existiert. Ich denke jedoch, dass die Art und Weise, wie ich es verwende, im Einklang mit seiner beabsichtigten Verwendung steht.

Ist das ein Fehler in gcc, ein nachsichtigerImplementierung in zeitgenössischen clang-Versionen, oder habe ich diese Art von Check falsch implementiert? Es scheint, als wäre dies definitiv in Hana's Steuerhaus; ich versuche nur, meinen Kopf um sein neues Modell zu wickeln constexpr Metaprogrammierung.

Antworten:

2 für die Antwort № 1

Hier ist eine Problemumgehung, die eine Struktur "Funktor" anstelle von Lambda und eine zusätzliche Ebene der Indirektion für den Typ der verwendet is_valid Instanz, um gcc zu beschwichtigen.

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