我上了一堂课:
template <typename T>
class Task {
Task(const std::function<T()>& func)
: m_func(func)
{
// some stuff here
}
std::shared_ptr<T> getValue() {
return m_value;
}
void execute() {
m_value = std::make_shared<T>(m_func());
}
std::shared_ptr<T> m_value;
std::function<T()> m_func;
}
现在,我想将此Task类别名为shared_ptr,所以我要执行以下操作...
template <typename T> using TaskPtr = std::shared_ptr<Task<T> >;
我还有另一个类将存储TaskPtr的容器,我希望api的使用者在调用addTask时指定T,如下所示。
Class X {
// some boiler plate code
template <typename T>
addTask(TaskPtr<T> task) {
m_queue.push(task);
}
void loop() {
// do some stuff
auto item = m_queue.front();
item->execute();
m_queue.pop();
// continue looping
}
std::queue<TaskPtr<T> > m_queue;
}
我想知道这样做的最好方法是什么。此代码给我的错误是T未定义。 h!我需要在template <tyepname T>
定义上方添加m_queue
,这很有意义。当我这样做时,我发现我将关键字typedef
放在错误的位置。当我删除模板声明和T
仅包含std::queue<Taskptr> m_queue;
时,它告诉我缺少模板参数。这很有意义,除了我不知道应该去哪里。
我已经搜索了答案,但找不到任何东西。我正在尝试做什么正确的语法实现?
答案 0 :(得分:2)
错误位于:
class X {
....
std::queue<TaskPtr<T> > m_queue; // <--- T is unknown
};
到那时,编译器想知道任务的类型是什么,但是您只想存储所有任务,而不管它们的类型如何。要弄清楚如何进行这项工作,请查看T
的用法,并了解如何消除它。
template <typename T>
class Task {
std::shared_ptr<T> getValue() {
return m_value;
}
void execute() {
m_value = std::make_shared<T>(m_func());
}
....
};
如果只有execute
,那么生活就会很简单,execute()
的调用者并不关心T
是什么,只执行操作即可。如果仅仅是那样,那么解决方案将是微不足道的:
class TaskBase
{
public:
virtual ~TaskBase() = default;
TaskBase(const TaskBase &) = default; // and so on....
virtual void execute() = 0;
};
template <typename T>
class Task : public TaskBase {
....
};
然后,只需存储指向TaskBase
而不是Task<T>
的指针。
解决getValue()
的过程要复杂得多。您需要使用从TaskBase到getValue<T>()
的实际Task的动态转换:
template <typename T>
std::shared_ptr<T> Task<T>::getValue() {
return m_value;
}
template<typename T>
std::shared_ptr<T> TaskBase::getValue()
{
auto childThis = dynamic_cast<Task<T>*>(this);
if (childThis == nullptr) {
// or maybe throw an exception
return nullptr;
}
return childThis->getValue();
}
使用起来比较棘手,因为用户必须知道任务中存储了什么类型:
void foo(std::shared_ptr<TaskBase> ptr)
{
auto ifInt = ptr->getValue<int>();
auto ifDouble = ptr->getValue<double>();
... more code ..
}
在这种情况下,Task<int>
将检测到ifInt
,但是对于Task<unsigned>
,这将因为ifInt==nullptr
而失败。
#include <memory>
#include <functional>
#include <queue>
#include <iostream>
class TaskBase
{
public:
virtual ~TaskBase() = default;
TaskBase() = default;
TaskBase(const TaskBase &) = default; // and so on....
virtual void execute() = 0;
template <typename T>
std::shared_ptr<T> getValue();
};
template <typename T>
class Task : public TaskBase {
public:
Task(const std::function<T()>& func)
: m_func(func)
{
// some stuff here
}
void execute() override {
m_value = std::make_shared<T>(m_func());
}
std::shared_ptr<T> getValue() {
return m_value;
}
private:
std::shared_ptr<T> m_value;
std::function<T()> m_func;
};
template <typename T>
std::shared_ptr<T> TaskBase::getValue()
{
auto downCast = dynamic_cast<Task<T>*>(this);
if (downCast)
return downCast->getValue();
else
return nullptr;
}
using TaskPtr = std::shared_ptr<TaskBase>;
class X {
// some boiler plate code
public:
void addTask(TaskPtr task) {
m_queue.push(task);
}
void loop() {
// do some stuff
auto item = m_queue.front();
item->execute();
m_queue.pop();
// continue looping
}
std::queue<TaskPtr> m_queue;
};
int main()
{
X x;
TaskPtr task = std::make_shared<Task<int>>(
[] { std::cout << "int task execution\n"; return 5;});
x.addTask(task);
x.loop();
std::cout << "getValue<int> --> ";
auto valPtr = task->getValue<int>();
if (valPtr)
std::cout << *valPtr << '\n';
else
std::cout << "nullptr\n";
std::cout << "getValue<float> --> ";
auto valPtr2 = task->getValue<float>();
if (valPtr2)
std::cout << *valPtr2 << '\n';
else
std::cout << "nullptr\n";
}