存储稍后要转发的变量参数

Storing variable arguments to be forwarded later

本文关键字:变量 参数 转发 存储      更新时间:2023-10-16

我正在开发一个小程序,它使用操作队列按顺序执行操作。

我希望能够在我的操作中存储参数,直到它们被执行(然后应该可以从操作的exec()方法访问参数(。

我在下面有一个小例子:

#include <tuple>
#include <iostream>
#include <memory>
#include <utility>
#include <queue>
/**
* Action interface
*/
struct Action {
Action() {}
virtual void exec() = 0;
};

/**
* This action creates an object which type is given as template
* and passes the parameters given to its ctor. On completion, it
* prints its ID.
*/
template<class T, class... Ps>
struct CustomAction : public Action {
// trying to store the variable arguments
std::tuple<Ps...> _args;
int _actionId;
CustomAction(int id, Ps&&... args) : _actionId(id), 
_args(std::make_tuple(std::forward<Ps>(args)...)) {
}
virtual void exec() override {
T* item = new T(std::forward<Ps>(_args)...);
std::cout << "done " << _actionId << std::endl;
}
};
struct ActionQueue {
std::queue<Action*> _queue;
ActionQueue() {
}
void exec() {
while(_queue.size()) {
auto action = _queue.front();
action->exec();
_queue.pop();
}
}
template<class T, class... Ps>
void push(Ps&&... args) {
auto action = new T(std::forward<Ps>(args)...);
_queue.push(action);
}
};
/**
* Example item that is to be created. Nothing important here
*/
struct Item {
int _b;
Item(int b) : _b(b) {
}
};
int main() {

ActionQueue aq;
int actionId = 5;
int param = 2;
aq.push<CustomAction<Item>>(actionId, param);
// do stuff
aq.exec();
}

在此示例中,我创建了一个ActionQueue。我在队列中推送一个新CustomAction。此操作只是创建一个Item,并为其 ctor 提供我在操作队列上推送操作时给出的参数。

我的问题是我不知道为什么提供给push()方法的参数不能在CustomAction类中使用。

编译上面的示例给了我以下错误:

<source>:56:27: error: no matching constructor for initialization of 'CustomAction<Item>'
auto action = new T(std::forward<Ps>(args)...);
^ ~~~~~~~~~~~~~~~~~~~~~~
<source>:82:8: note: in instantiation of function template specialization 'ActionQueue::push<CustomAction<Item>, int &, int &>' requested here
aq.push<CustomAction<Item>>(actionId, param);
^
<source>:27:5: note: candidate constructor not viable: requires single argument 'id', but 2 arguments were provided
CustomAction(int id, Ps&&... args) : _actionId(id), 
^
<source>:22:8: note: candidate constructor (the implicit copy constructor) not viable: requires 1 argument, but 2 were provided
struct CustomAction : public Action {
^
<source>:22:8: note: candidate constructor (the implicit move constructor) not viable: requires 1 argument, but 2 were provided
1 error generated.
Compiler returned: 1

该错误说CustomAction需要一个参数,而给出了两个参数,但CustomAction应该接受args中的第二个参数。

我在这里做错了什么?

谢谢

我在这里做错了什么?

井。。。首先,您需要解压缩元组;例如,使用帮助程序函数,如下所示(std::index_sequencestd::make_index_sequence从 C++14 开始可用;您正在编译 C++17,以便您可以使用它们(

template <std::size_t ... Is>
void exec_helper (std::index_sequence<Is...> const &)
{ T* item = new T{std::get<Is>(_args)...}; }
virtual void exec() override {
exec_helper(std::make_index_sequence<sizeof...(Ps)>{});
std::cout << "done " << _actionId << std::endl;
}

但还有其他问题。

通过示例:您只能对函数/方法的模板参数使用完美转发,因此不在此构造函数中

CustomAction(int id, Ps&&... args) : _actionId(id), 
_args(std::make_tuple(std::forward<Ps>(args)...)) {
}

因为Ps...是类的模板参数,而不是构造函数的模板参数。

应该工作的东西

template <typename ... Us>
CustomAction (int id, Us && ... args) : _actionId{id}, 
_args{std::forward<Us>(args)...}
{ }

我想

aq.push<CustomAction<Item>>(actionId, param);

应该是

// ........................VVV
aq.push<CustomAction<Item, int>>(actionId, param);

重构并更正了内联注释:

#include <tuple>
#include <iostream>
#include <memory>
#include <utility>
#include <queue>
/**
* Action interface
*/
struct Action {
Action() {}
// Comment #8 - virtual destructor required for polymorphic objects!
virtual ~Action() {}
virtual void exec() = 0;
};

/**
* This action creates an object which type is given as template
* and passes the parameters given to its ctor. On completion, it
* prints its ID.
*/
template<class T, class... Ps>
struct CustomAction : public Action {
// Comment #1 - Ps will be full types. Not references.
// trying to store the variable arguments
std::tuple<Ps...> _args;
int _actionId;
// Comment #2 - deduced arguments in the constructor allow perfect forwarding.
template<class...Args>
CustomAction(int id, Args&&... args) 
: _actionId(id)
, _args(std::forward<Args>(args)...) {
}
virtual void exec() override {
// Comment #3 - The use of a lambda allows argument expansion for a constructor.
auto create = [](auto&&...args)
{
return new T(std::forward<decltype(args)>(args)...);
};
// Comment #4 - c++17's std::apply allows us to call the lambda with arguments forwarded out of the tuple
T* item = std::apply(create, std::move(_args));
// Comment #9 do something with this pointer...
std::cout << "done " << _actionId << std::endl;
}
};
struct ActionQueue {
std::queue<Action*> _queue;
ActionQueue() {
}
void exec() {
while(_queue.size()) {
auto action = _queue.front();
action->exec();
_queue.pop();
}
}
// Comment #5 - separate concerns - the queue only cares about the Action interface
void push(Action* pa)
{
_queue.push(pa);
}
};
/**
* Example item that is to be created. Nothing important here
*/
struct Item {
int _b;
Item(int b) : _b(b) {
}
};
// Comment #6 - A helper function to deduce stored argument types
template<class Action, class...Args>
auto makeCustomAction(int id, Args&&...args)
{
using CustomActionType = CustomAction<Action, std::decay_t<Args>...>;
return new CustomActionType(id, std::forward<Args>(args)...);
}
int main() {

ActionQueue aq;
int actionId = 5;
int param = 2;
// Comment #7 - store-and-forwarded arguments are now auto deduced.
aq.push(makeCustomAction<Item>(actionId, param));
// do stuff
aq.exec();
}

https://godbolt.org/z/ZLkfi5