2

在我的应用程序中,我想在编译时确定任意仿函数类型是否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_iffoo. 我尝试了以下编译器:

  • 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新元编程模型。

4

1 回答 1

2

这是一种解决方法,它使用结构“函子”而不是 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{};
}
于 2017-04-12T17:45:26.693 回答