如何在容器中指定模板化别名的泛型类型

时间:2018-08-30 12:01:02

标签: c++ c++11 templates polymorphism type-erasure

我上了一堂课:

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;时,它告诉我缺少模板参数。这很有意义,除了我不知道应该去哪里。

我已经搜索了答案,但找不到任何东西。我正在尝试做什么正确的语法实现?

1 个答案:

答案 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";   
}