Vin*_*ent 9 c++ functor sfinae template-meta-programming c++17
问题描述:
C++17介绍std::invocable<F, Args...>,很好地检测类型...是否可以使用给定的参数进行调用.但是,是否有办法为仿函数的任何参数执行此操作(因为标准库的现有特征的组合已经允许检测函数,函数指针,函数引用,成员函数......)?
换句话说,如何实现以下类型特征?
template <class F>
struct is_functor {
    static constexpr bool value = /*using F::operator() in derived class works*/;
};
使用示例:
#include <iostream>
#include <type_traits>
struct class0 {
    void f();
    void g();
};
struct class1 {
    void f();
    void g();
    void operator()(int);
};
struct class2 {
    void operator()(int);
    void operator()(double);
    void operator()(double, double) const noexcept;
};
struct class3 {
    template <class... Args> constexpr int operator()(Args&&...);
    template <class... Args> constexpr int operator()(Args&&...) const;
};
union union0 {
    unsigned int x;
    unsigned long long int y;
    template <class... Args> constexpr int operator()(Args&&...);
    template <class... Args> constexpr int operator()(Args&&...) const;
};
struct final_class final {
    template <class... Args> constexpr int operator()(Args&&...);
    template <class... Args> constexpr int operator()(Args&&...) const;
};
int main(int argc, char* argv[]) {
     std::cout << is_functor<int>::value;
     std::cout << is_functor<class0>::value;
     std::cout << is_functor<class1>::value;
     std::cout << is_functor<class2>::value;
     std::cout << is_functor<class3>::value;
     std::cout << is_functor<union0>::value;
     std::cout << is_functor<final_class>::value << std::endl;
     return 0;
}
应该输出001111X.在一个理想的世界里,X应该是1,但我不认为它是可行的C++17(参见奖金部分).
编辑:
这篇文章似乎提出了解决问题的策略.但是,有没有更好/更优雅的方式来做到这一点C++17?
奖金:
作为奖励,是否有办法使其适用于final类型(但这是完全可选的,可能不可行)?
根据我对这个问题的回答,我能够解决您的问题,包括奖金问题:-)
以下是在另一个线程中发布的代码,加上一些小调整,以便在无法调用对象时获得特殊值。代码需要c++17,所以目前没有MSVC...
#include<utility>
constexpr size_t max_arity = 10;
struct variadic_t
{
};
struct not_callable_t
{
};
namespace detail
{
    // it is templated, to be able to create a
    // "sequence" of arbitrary_t's of given size and
    // hece, to 'simulate' an arbitrary function signature.
    template <size_t>
    struct arbitrary_t
    {
        // this type casts implicitly to anything,
        // thus, it can represent an arbitrary type.
        template <typename T>
        operator T&& ();
        template <typename T>
        operator T& ();
    };
    template <typename F, size_t... Is,
                typename U = decltype(std::declval<F>()(arbitrary_t<Is>{}...))>
    constexpr auto test_signature(std::index_sequence<Is...>)
    {
        return std::integral_constant<size_t, sizeof...(Is)>{};
    }
    template <size_t I, typename F>
    constexpr auto arity_impl(int) -> decltype(test_signature<F>(std::make_index_sequence<I>{}))
    {
        return {};
    }
    template <size_t I, typename F, std::enable_if_t<(I == 0), int> = 0>
    constexpr auto arity_impl(...) {
        return not_callable_t{};
    }
    template <size_t I, typename F, std::enable_if_t<(I > 0), int> = 0>
    constexpr auto arity_impl(...)
    {
        // try the int overload which will only work,
        // if F takes I-1 arguments. Otherwise this
        // overload will be selected and we'll try it 
        // with one element less.
        return arity_impl<I - 1, F>(0);
    }
    template <typename F, size_t MaxArity = 10>
    constexpr auto arity_impl()
    {
        // start checking function signatures with max_arity + 1 elements
        constexpr auto tmp = arity_impl<MaxArity + 1, F>(0);
        if constexpr(std::is_same_v<std::decay_t<decltype(tmp)>, not_callable_t>) {
            return not_callable_t{};
        }
        else if constexpr (tmp == MaxArity + 1)
        {
            // if that works, F is considered variadic
            return variadic_t{};
        }
        else
        {
            // if not, tmp will be the correct arity of F
            return tmp;
        }
    }
}
template <typename F, size_t MaxArity = max_arity>
constexpr auto arity(F&& f) { return detail::arity_impl<std::decay_t<F>, MaxArity>(); }
template <typename F, size_t MaxArity = max_arity>
constexpr auto arity_v = detail::arity_impl<std::decay_t<F>, MaxArity>();
template <typename F, size_t MaxArity = max_arity>
constexpr bool is_variadic_v = std::is_same_v<std::decay_t<decltype(arity_v<F, MaxArity>)>, variadic_t>;
// HERE'S THE IS_FUNCTOR
template<typename T>
constexpr bool is_functor_v = !std::is_same_v<std::decay_t<decltype(arity_v<T>)>, not_callable_t>;
给定 yout 问题中的类,以下代码可以成功编译(您甚至可以使用可变参数 lambda:
constexpr auto lambda_func = [](auto...){};
void test_is_functor() {
    static_assert(!is_functor_v<int>);
    static_assert(!is_functor_v<class0>);
    static_assert(is_functor_v<class1>);
    static_assert(is_functor_v<class2>);
    static_assert(is_functor_v<class3>);
    static_assert(is_functor_v<union0>);
    static_assert(is_functor_v<final_class>);
    static_assert(is_functor_v<decltype(lambda_func)>);
}
另请参阅此处的运行示例。
| 归档时间: | 
 | 
| 查看次数: | 460 次 | 
| 最近记录: |