如何使用 Boost.Hana 来确定仿函数是否具有可以使用特定模板参数调用的调用运算符?
How can I use Boost.Hana to determine whether a functor has a call operator that can be invoked with a particular template argument?
在我的应用程序中,我想在编译时确定任意仿函数类型 Func
是否具有可以使用给定显式模板参数 T
调用的无效调用运算符。 ,我想到了以下内容:
#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
模板参数类型在 foo
中明确 enable_if
-ed 了。我试过以下编译器:
- 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 版本中更宽松的实现,还是我错误地实现了这种类型的检查?看起来这绝对是在 Hana 的驾驶室内;我只是想了解它的新模型 constexpr
元编程。
这里有一个变通方法,它使用结构 "functor" 而不是 lambda 和额外的间接层 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{};
}
在我的应用程序中,我想在编译时确定任意仿函数类型 Func
是否具有可以使用给定显式模板参数 T
调用的无效调用运算符。
#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
模板参数类型在 foo
中明确 enable_if
-ed 了。我试过以下编译器:
- 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 版本中更宽松的实现,还是我错误地实现了这种类型的检查?看起来这绝对是在 Hana 的驾驶室内;我只是想了解它的新模型 constexpr
元编程。
这里有一个变通方法,它使用结构 "functor" 而不是 lambda 和额外的间接层 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{};
}