是否有可能找出多态C ++ 14 lambda的参数类型和返回类型?

时间:2015-01-23 07:57:00

标签: c++ lambda metaprogramming c++14 template-meta-programming

从这个问题(Is it possible to figure out the parameter type and return type of a lambda?)开始,我经常使用建议的function_traits。然而,随着C ++ 14的多态性lambda已经到来,他们给了我一个艰难的时间。

template <typename T>
struct function_traits
    : public function_traits<decltype(&T::operator())>
{};
// For generic types, directly use the result of the signature of its 'operator()'

template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const>
// we specialize for pointers to member function
{
    enum { arity = sizeof...(Args) };
    // arity is the number of arguments.

    typedef ReturnType result_type;

    template <size_t i>
    struct arg
    {
        typedef typename std::tuple_element<i, std::tuple<Args...>>::type type;
        // the i-th argument is equivalent to the i-th tuple element of a tuple
        // composed of those arguments.
    };
};

另一个问题答案中提出的operator()现已超载,正如标准中所述,以支持:

auto lambda1 = [](auto& a) { a.foo(); } 

auto lambda2 = [](auto&&... args) { foo(args...); };

此重载现在将function_traits类分开,因为编译器无法解析operator()的正确版本。

lambda.cpp:98:38: error: reference to overloaded function could not be
      resolved; did you mean to call it?
      typedef function_traits<decltype(&T::operator())> caller;

用C ++ 14在多态lambda上实现function_traits的功能是否可行?

2 个答案:

答案 0 :(得分:1)

目前在一般意义上这是不可能做到的,但是如果你的lambdas对所有参数都有auto并且可以使用某些已知类型替换它们,那么你可以修改这个问题的答案: C++ metafunction to determine whether a type is callable

沿着

的路线
static const bool OneArg = (sizeof( test<T, int>(0)  ) == 1);
static const bool TwoArg = (sizeof( test<T, int, int>(0)  ) == 1);
static const bool ThreeArg = (sizeof( test<T, int, int, int>(0)  ) == 1);
static constexpr std::size_t TemplatedOperatorArgSize = 
    OneArg
        ? 1
        : TwoArg
            ? 2
            : ThreeArg
                ? 3
                : -1;

为嵌套三元组道歉。然后用这样的东西来称呼它:

template<size_t N, typename T>
struct Apply {
    template<typename F, typename... A>
    static inline decltype(auto) apply(F && f, A &&... a) {
        return Apply<N-1, T>::apply(::std::forward<F>(f), std::declval<T>(), ::std::forward<A>(a)...);
    }
};

template<typename T>
struct Apply<0, T> {
    template<typename F, typename... A>
    static inline decltype(auto) apply(F && f, A &&... a) {
        return invoke(std::forward<F>(f), std::forward<A>(a)...);
    }
};

template<std::size_t Size, typename T, typename F>
inline decltype(auto) apply(F && f) {
    return Apply<Size, T>::apply(::std::forward<F>(f));
}

使用来自http://en.cppreference.com/w/cpp/utility/functional/invoke

的调用

获取返回类型

using return_type = decltype(
    apply<has_callable_operator<T>::TemplatedOperatorArgSize, int>(function)
);

可以使用std::make_index_sequence

重写第一部分以处理任意数量的参数

但一般来说,我说你要去兔子洞了。祝你好运。

注意:没有测试应用代码,但它应该足以让你去。

编辑:此外,返回类型需要独立于参数类型才能实现您想要的效果

答案 1 :(得分:1)

在使用 获取功能 实施 缓存 时,我遇到了类似的情况。 类似的东西:

template<class KEY, class VALUE, class FetchFunctor>
class Cache { ... };

第1阶段

我想保存用户需要说明他/她 FetchFunctor 的类型,而没有C++17 class template argument deduction我去了 < em> createCahce 这样的辅助方法:

// [1]
template<class KEY, class VALUE, class FetchFunctor>
auto createCache(FetchFunctor fetchFunctor) {
    return Cache<KEY, VALUE, FetchFunctor>(fetchFunctor);
}

因此,创建缓存非常容易,例如:

auto cache = createCache<int, int>([](int i){return i+3;});

第2阶段

允许用户创建缓存而不需要提供 密钥 类型的缓存,并从提供的 FetchFunctor 中推断出它们。所以我添加了一个额外的 createCache 方法:

// [2]
template<class FetchFunctor>
auto createCache(FetchFunctor fetchFunctor) {
    // function_traits is a namespace where I 'hide' the
    // traits structs for result type and arguments type deduction
    using f = function_traits::traits<decltype(fetchFunctor)>;
    using KEY = typename f::template arg<0>::type;
    using VALUE = typename f::result_type;
    return Cache<KEY, VALUE, FetchFunctor>(fetchFunctor);
}

现在创建缓存更加容易:

auto cache = createCache([](int i){return i+3;});

但是,上面的'createCache'方法可以同时存在吗?

幸运的是。

  1. 如果用户提供模板参数键,值,则第一个是唯一匹配。
  2. 如果用户没有提供模板参数,编译器会将第二个视为'更专业化,并且会更喜欢它。
  3. 这样我们就可以支持以下所有情况:

        // [a]
        auto cache = createCache([](int i){return i+3;});
        // compiler deduces Key, Value to be: int, int - using the 2nd createCache
    
        // [b]
        auto cache = createCache<int, int>([](auto i){return i+3;});
        // we have a generic lambda so we provide Key and Value - using the 1st createCache
    
        // [c]
        auto cache = createCache<string, string>(
            [](const char* s){return string(s) + '!';}
        );
        // we want different Key and/or Value than would be deduced - we use 1st createCache
    

    还是...

    以下内容无法编译:

        auto cache = createCache([](auto i){return i+3;});
        // we cannot deduce types for generic lambda
        // compiler goes to the 2nd createCache but Key and Value cannot be deduced
        // - compilation error
    

    但这不应该打扰我们......你可以提供上面[b]中的键和值。

    代码:

    http://coliru.stacked-crooked.com/a/e19151a5c245d7c3