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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在容器中指定模板化别名'泛型类型相关的知识,希望对你有一定的参考价值。

我有一个课程任务:

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未定义的错误。咄!我需要在我的template <tyepname T>定义之上添加m_queue,这是有道理的。当我这样做时,我得到的是我将关键字typedef放在一个不正确的位置。当我删除模板声明和T只有std::queue<Taskptr> m_queue;时,它告诉我我错过了一个模板参数。这是有道理的,除了我不明白应该去哪里。

我找了一个答案,找不到任何东西。我正在尝试做什么是正确的语法实现?

答案

错误发生在:

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>()的实际任务:

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会失败。


Apparently the above explanation is not clear enough, so here is the complete source that compiles and works:
#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
"; return 5;});

   x.addTask(task);
   x.loop();
   std::cout << "getValue<int> --> ";
   auto valPtr = task->getValue<int>();
   if (valPtr)
      std::cout << *valPtr << '
';
   else
      std::cout << "nullptr
";   
   std::cout << "getValue<float> --> ";
   auto valPtr2 = task->getValue<float>();
   if (valPtr2)
      std::cout << *valPtr2 << '
';
   else
      std::cout << "nullptr
";   
}

以上是关于如何在容器中指定模板化别名'泛型类型的主要内容,如果未能解决你的问题,请参考以下文章

C++泛型编程

C++泛型编程

我可以在 XAML(.NET 4 之前)中指定泛型类型吗?

C++从青铜到王者第六篇:C++模板初阶

在定义单个 AWS::Serverless::Function 的 SAM 模板中指定多个 API 阶段和 Lambda 别名

C++ Primer 5th笔记(chap 16 模板和泛型编程)模板类型别名