C++17 - 如何扣除作为模板参数传递的 lambda 的可变参数包?
C++17 - How to deduct the variadic parameter pack of a lambda passed as a template parameter?
我有一个class memoize
,它扣除作为模板参数传递给它的函数指针的可变参数列表(见下面的代码)。
我希望能够为 lambda 做同样的事情,但我似乎没有找到正确的方法。理想情况下,它应该在取消注释并完成下面 main() 函数的最后一行时起作用(或参见 https://godbolt.org/z/Y8G47h4GK)。
#include <tuple>
#include <iostream>
template<typename... Ts> struct pack { };
template <class> struct pack_helper;
template <class R, class... Args>
struct pack_helper<R(Args...)> {
using args = pack<Args...>;
};
template <class R, class... Args>
struct pack_helper<R(*)(Args...)> {
using args = pack<Args...>;
};
template <class F, class = typename pack_helper<F>::args>
class memoize;
template <class F, class... Args>
class memoize<F, pack<Args...>>
{
public:
using result_type = std::invoke_result_t<F, Args...>;
memoize(F &&f) : _f(std::forward<F>(f)) {}
result_type operator()(Args... args) { return _f(args...); }
private:
F _f;
};
int test(int x) { return x + 1; }
int main() {
auto test_mem = memoize<decltype(&test)>(&test);
std::cout << test_mem(2) << '\n';
auto l = [](int x) -> int { return x + 1; };
// auto l_mem = memoize<?>(?);
}
下面应该可以解决问题。请注意,pack_helper
逻辑已被修改,即使对于函数指针也是如此。
template<typename... Ts> struct pack { };
// helper functions to retrieve arguments for member function pointers
template<class Result, class Type, class ...Args>
pack<Args...> lambda_pack_helper(Result(Type::*) (Args...));
template<class Result, class Type, class ...Args>
pack<Args...> lambda_pack_helper(Result(Type::*) (Args...) const);
// helper functions: if called with an instance of the first parameter type of memorize, the return type is the one used as the second one
// overload for function
template<class Result, class ...Args>
pack<Args...> pack_helper(Result(Args...));
// overload for function pointer
template<class Result, class ...Args>
pack<Args...> pack_helper(Result (*)(Args...));
// sfinae will prevent this overload from being considered for anything not providing an operator()
// for those types we use the return type of helper function lambda_pack_helper
template<class T>
decltype(lambda_pack_helper(&T::operator())) pack_helper(T);
template <class F, class = decltype(pack_helper(std::declval<F>()))>
class memoize;
template <class F, class... Args>
class memoize<F, pack<Args...>>
{
public:
using result_type = std::invoke_result_t<F, Args...>;
memoize(F&& f) : _f(std::move(f)) {}
memoize(F const& f) : _f(f) {}
result_type operator()(Args... args) { return _f(args...); }
private:
F _f;
};
int test(int x) { return x + 1; }
int main() {
auto test_mem = memoize<decltype(&test)>(&test);
std::cout << test_mem(2) << '\n';
auto l = [](int x) -> int { return x + 1; };
auto l_mem = memoize<decltype(l)>(l);
std::cout << l_mem(3) << '\n';
}
我找到了另一种方法(见下文),但我认为上面 fabian 的回答更好!
#include <iostream>
template<typename... Ts> struct pack { };
template <class> struct pack_helper;
template <class R, class... Args>
struct pack_helper<R(Args...)> {
using args = pack<Args...>;
};
template <class R, class... Args>
struct pack_helper<R(*)(Args...)> {
using args = pack<Args...>;
};
// ---- for lambdas
template <class T>
struct pack_helper : public pack_helper<decltype(&T::operator())>
{};
template <class LambdaClass, class R, class... Args>
struct pack_helper<R(LambdaClass::*)(Args...) const> {
using args = pack<Args...>;
};
template <class F, class = typename pack_helper<F>::args>
class memoize;
template <class F, class... Args>
class memoize<F, pack<Args...>>
{
public:
using result_type = decltype(std::declval<F>()(std::declval<Args>()...));
memoize(F&& f) : _f(std::forward<F>(f)) {}
memoize(F const& f) : _f(f) {}
result_type operator()(Args... args) { return _f(args...); }
private:
F _f;
};
int test(int x) { return x + 1; }
int main() {
auto test_mem = memoize<decltype(&test)>(&test);
std::cout << test_mem(2) << '\n';
auto l = [](int x) -> int { return x + 1; };
auto l_mem = memoize<decltype(l)>(l);
std::cout << l_mem(3) << '\n';
}
我有一个class memoize
,它扣除作为模板参数传递给它的函数指针的可变参数列表(见下面的代码)。
我希望能够为 lambda 做同样的事情,但我似乎没有找到正确的方法。理想情况下,它应该在取消注释并完成下面 main() 函数的最后一行时起作用(或参见 https://godbolt.org/z/Y8G47h4GK)。
#include <tuple>
#include <iostream>
template<typename... Ts> struct pack { };
template <class> struct pack_helper;
template <class R, class... Args>
struct pack_helper<R(Args...)> {
using args = pack<Args...>;
};
template <class R, class... Args>
struct pack_helper<R(*)(Args...)> {
using args = pack<Args...>;
};
template <class F, class = typename pack_helper<F>::args>
class memoize;
template <class F, class... Args>
class memoize<F, pack<Args...>>
{
public:
using result_type = std::invoke_result_t<F, Args...>;
memoize(F &&f) : _f(std::forward<F>(f)) {}
result_type operator()(Args... args) { return _f(args...); }
private:
F _f;
};
int test(int x) { return x + 1; }
int main() {
auto test_mem = memoize<decltype(&test)>(&test);
std::cout << test_mem(2) << '\n';
auto l = [](int x) -> int { return x + 1; };
// auto l_mem = memoize<?>(?);
}
下面应该可以解决问题。请注意,pack_helper
逻辑已被修改,即使对于函数指针也是如此。
template<typename... Ts> struct pack { };
// helper functions to retrieve arguments for member function pointers
template<class Result, class Type, class ...Args>
pack<Args...> lambda_pack_helper(Result(Type::*) (Args...));
template<class Result, class Type, class ...Args>
pack<Args...> lambda_pack_helper(Result(Type::*) (Args...) const);
// helper functions: if called with an instance of the first parameter type of memorize, the return type is the one used as the second one
// overload for function
template<class Result, class ...Args>
pack<Args...> pack_helper(Result(Args...));
// overload for function pointer
template<class Result, class ...Args>
pack<Args...> pack_helper(Result (*)(Args...));
// sfinae will prevent this overload from being considered for anything not providing an operator()
// for those types we use the return type of helper function lambda_pack_helper
template<class T>
decltype(lambda_pack_helper(&T::operator())) pack_helper(T);
template <class F, class = decltype(pack_helper(std::declval<F>()))>
class memoize;
template <class F, class... Args>
class memoize<F, pack<Args...>>
{
public:
using result_type = std::invoke_result_t<F, Args...>;
memoize(F&& f) : _f(std::move(f)) {}
memoize(F const& f) : _f(f) {}
result_type operator()(Args... args) { return _f(args...); }
private:
F _f;
};
int test(int x) { return x + 1; }
int main() {
auto test_mem = memoize<decltype(&test)>(&test);
std::cout << test_mem(2) << '\n';
auto l = [](int x) -> int { return x + 1; };
auto l_mem = memoize<decltype(l)>(l);
std::cout << l_mem(3) << '\n';
}
我找到了另一种方法(见下文),但我认为上面 fabian 的回答更好!
#include <iostream>
template<typename... Ts> struct pack { };
template <class> struct pack_helper;
template <class R, class... Args>
struct pack_helper<R(Args...)> {
using args = pack<Args...>;
};
template <class R, class... Args>
struct pack_helper<R(*)(Args...)> {
using args = pack<Args...>;
};
// ---- for lambdas
template <class T>
struct pack_helper : public pack_helper<decltype(&T::operator())>
{};
template <class LambdaClass, class R, class... Args>
struct pack_helper<R(LambdaClass::*)(Args...) const> {
using args = pack<Args...>;
};
template <class F, class = typename pack_helper<F>::args>
class memoize;
template <class F, class... Args>
class memoize<F, pack<Args...>>
{
public:
using result_type = decltype(std::declval<F>()(std::declval<Args>()...));
memoize(F&& f) : _f(std::forward<F>(f)) {}
memoize(F const& f) : _f(f) {}
result_type operator()(Args... args) { return _f(args...); }
private:
F _f;
};
int test(int x) { return x + 1; }
int main() {
auto test_mem = memoize<decltype(&test)>(&test);
std::cout << test_mem(2) << '\n';
auto l = [](int x) -> int { return x + 1; };
auto l_mem = memoize<decltype(l)>(l);
std::cout << l_mem(3) << '\n';
}