为什么我不能 std::apply 用于成员函数
Why can't I std::apply for a member function
我正在尝试开发一个包装器来帮助人们使用 pthread
调用任何成员函数。
template <typename>
struct signature;
template <typename C, typename R, typename... Args>
struct signature<R (C::*)(Args...)> {
using return_type = R;
using pure_argument_type = std::tuple<C*, std::tuple<Args...>>; // obj, {args...}
using argument_type =
std::tuple<R (C::*)(Args...), pure_argument_type>; // obj.mem_fn, {obj, args...}
};
struct Job {
public:
Job() = default;
~Job() = default;
void join() { pthread_join(btd, nullptr); }
template <typename C, typename F, typename... Args>
int run(F C::*f, C* c, Args&&... args) {
typename signature<decltype(f)>::pure_argument_type pureParams =
std::make_tuple(c, std::forward_as_tuple(args...));
typename signature<decltype(f)>::argument_type params = std::make_tuple(f, pureParams);
return pthread_create(
&btd, nullptr,
[](void* p) {
auto param = static_cast<typename signature<decltype(f)>::argument_type*>(p);
std::apply(std::get<0>(*param), std::get<1>(*param)); ////// ERROR!
return (void*)nullptr;
},
¶ms);
}
private:
pthread_t btd;
};
但是,当我尝试执行以下操作时,
class Test {
public:
void func(int& a, double& d) { a = 2; d = 2.0; }
void dojob() {
int a = 0;
double d = 0.0;
Job job;
job.run(&Test::func, this, std::ref(a), std::ref(d));
job.join();
std::cout << "a:" << a << " d:" << d << std::endl;
}
};
Test t;
t.dojob();
我收到错误:
In file included from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/stl_map.h:63,
from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/map:61,
from <source>:2:
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/tuple: In instantiation of 'constexpr decltype(auto) std::__apply_impl(_Fn&&, _Tuple&&, std::index_sequence<_Idx ...>) [with _Fn = void (Test::*&)(int&, double&); _Tuple = std::tuple<Test*, std::tuple<int&, double&> >&; long unsigned int ..._Idx = {0, 1}; std::index_sequence<_Idx ...> = std::integer_sequence<long unsigned int, 0, 1>]':
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/tuple:1854:31: required from 'constexpr decltype(auto) std::apply(_Fn&&, _Tuple&&) [with _Fn = void (Test::*&)(int&, double&); _Tuple = std::tuple<Test*, std::tuple<int&, double&> >&]'
<source>:98:27: required from 'int Job::run(F C::*, C*, Args&& ...) [with C = Test; F = void(int&, double&); Args = {std::reference_wrapper<int>, std::reference_wrapper<double>}]'
<source>:118:16: required from here
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/tuple:1843:27: error: no matching function for call to '__invoke(void (Test::*&)(int&, double&), Test*&, std::__tuple_element_t<1, std::tuple<Test*, std::tuple<int&, double&> > >&)'
1843 | return std::__invoke(std::forward<_Fn>(__f),
| ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
1844 | std::get<_Idx>(std::forward<_Tuple>(__t))...);
|
你可以在这里得到我的代码:https://godbolt.org/z/sMjcxvP33
std::get<1>(*param)
的return类型是std::tuple<C*, std::tuple<Args...>>
。
由于这个tuple
的第二个元素的类型也是tuple
,所以需要用std::apply
再展开,像这样
std::apply(
[f = std::get<0>(*param)](auto* obj, auto&& args) {
std::apply([&](auto&&... args) {
(obj->*f)(std::forward<decltype(args)>(args)...);
}, std::forward<decltype(args)>(args));
},
std::get<1>(*param));
另一种方法是将嵌套的 tuple
展平为 std::tuple<C*, Args...>
,然后将其与 f
.
一起传递给 std::apply
std::apply(
std::get<0>(*param),
std::tuple_cat(
std::make_tuple(std::get<0>(std::get<1>(*param))),
std::get<1>(std::get<1>(*param))));
std::apply
需要一个包含所有参数的 单个 元组,而不是相互嵌套的元组。
你有一个竞争条件,如果你的 lambda 没有在 run
returns 时将参数复制到 std::apply
的参数中,你将取消引用悬空指针.
template <typename C, typename R, typename... Args>
struct signature<R (C::*)(Args...)> {
using return_type = R;
using pure_argument_type = std::tuple<C*, Args...>; // obj, args...
struct argument_type {
R (C::*fun)(Args...);
pure_argument_type args;
};
};
struct Job {
public:
Job() = default;
~Job() = default;
void join() { pthread_join(btd, nullptr); }
template <typename C, typename R, typename... Args>
int run(R (C::*f)(Args...), C* c, Args&&... args) {
using params_t = typename signature<R (C::*)(Args...)>::argument_type;
auto * params = new params_t{f, std::tuple_cat(std::make_tuple(c), std::forward_as_tuple(args...))};
return pthread_create(
&btd, nullptr,
[](void* p) {
auto param = static_cast<params_t*>(p);
std::apply(param->fun, param->args);
delete param;
return (void*)nullptr;
},
params);
}
private:
pthread_t btd;
};
我正在尝试开发一个包装器来帮助人们使用 pthread
调用任何成员函数。
template <typename>
struct signature;
template <typename C, typename R, typename... Args>
struct signature<R (C::*)(Args...)> {
using return_type = R;
using pure_argument_type = std::tuple<C*, std::tuple<Args...>>; // obj, {args...}
using argument_type =
std::tuple<R (C::*)(Args...), pure_argument_type>; // obj.mem_fn, {obj, args...}
};
struct Job {
public:
Job() = default;
~Job() = default;
void join() { pthread_join(btd, nullptr); }
template <typename C, typename F, typename... Args>
int run(F C::*f, C* c, Args&&... args) {
typename signature<decltype(f)>::pure_argument_type pureParams =
std::make_tuple(c, std::forward_as_tuple(args...));
typename signature<decltype(f)>::argument_type params = std::make_tuple(f, pureParams);
return pthread_create(
&btd, nullptr,
[](void* p) {
auto param = static_cast<typename signature<decltype(f)>::argument_type*>(p);
std::apply(std::get<0>(*param), std::get<1>(*param)); ////// ERROR!
return (void*)nullptr;
},
¶ms);
}
private:
pthread_t btd;
};
但是,当我尝试执行以下操作时,
class Test {
public:
void func(int& a, double& d) { a = 2; d = 2.0; }
void dojob() {
int a = 0;
double d = 0.0;
Job job;
job.run(&Test::func, this, std::ref(a), std::ref(d));
job.join();
std::cout << "a:" << a << " d:" << d << std::endl;
}
};
Test t;
t.dojob();
我收到错误:
In file included from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/stl_map.h:63,
from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/map:61,
from <source>:2:
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/tuple: In instantiation of 'constexpr decltype(auto) std::__apply_impl(_Fn&&, _Tuple&&, std::index_sequence<_Idx ...>) [with _Fn = void (Test::*&)(int&, double&); _Tuple = std::tuple<Test*, std::tuple<int&, double&> >&; long unsigned int ..._Idx = {0, 1}; std::index_sequence<_Idx ...> = std::integer_sequence<long unsigned int, 0, 1>]':
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/tuple:1854:31: required from 'constexpr decltype(auto) std::apply(_Fn&&, _Tuple&&) [with _Fn = void (Test::*&)(int&, double&); _Tuple = std::tuple<Test*, std::tuple<int&, double&> >&]'
<source>:98:27: required from 'int Job::run(F C::*, C*, Args&& ...) [with C = Test; F = void(int&, double&); Args = {std::reference_wrapper<int>, std::reference_wrapper<double>}]'
<source>:118:16: required from here
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/tuple:1843:27: error: no matching function for call to '__invoke(void (Test::*&)(int&, double&), Test*&, std::__tuple_element_t<1, std::tuple<Test*, std::tuple<int&, double&> > >&)'
1843 | return std::__invoke(std::forward<_Fn>(__f),
| ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
1844 | std::get<_Idx>(std::forward<_Tuple>(__t))...);
|
你可以在这里得到我的代码:https://godbolt.org/z/sMjcxvP33
std::get<1>(*param)
的return类型是std::tuple<C*, std::tuple<Args...>>
。
由于这个tuple
的第二个元素的类型也是tuple
,所以需要用std::apply
再展开,像这样
std::apply(
[f = std::get<0>(*param)](auto* obj, auto&& args) {
std::apply([&](auto&&... args) {
(obj->*f)(std::forward<decltype(args)>(args)...);
}, std::forward<decltype(args)>(args));
},
std::get<1>(*param));
另一种方法是将嵌套的 tuple
展平为 std::tuple<C*, Args...>
,然后将其与 f
.
std::apply
std::apply(
std::get<0>(*param),
std::tuple_cat(
std::make_tuple(std::get<0>(std::get<1>(*param))),
std::get<1>(std::get<1>(*param))));
std::apply
需要一个包含所有参数的 单个 元组,而不是相互嵌套的元组。
你有一个竞争条件,如果你的 lambda 没有在 run
returns 时将参数复制到 std::apply
的参数中,你将取消引用悬空指针.
template <typename C, typename R, typename... Args>
struct signature<R (C::*)(Args...)> {
using return_type = R;
using pure_argument_type = std::tuple<C*, Args...>; // obj, args...
struct argument_type {
R (C::*fun)(Args...);
pure_argument_type args;
};
};
struct Job {
public:
Job() = default;
~Job() = default;
void join() { pthread_join(btd, nullptr); }
template <typename C, typename R, typename... Args>
int run(R (C::*f)(Args...), C* c, Args&&... args) {
using params_t = typename signature<R (C::*)(Args...)>::argument_type;
auto * params = new params_t{f, std::tuple_cat(std::make_tuple(c), std::forward_as_tuple(args...))};
return pthread_create(
&btd, nullptr,
[](void* p) {
auto param = static_cast<params_t*>(p);
std::apply(param->fun, param->args);
delete param;
return (void*)nullptr;
},
params);
}
private:
pthread_t btd;
};