shared_ptr的多个类内typedef

时间:2014-10-06 09:29:07

标签: c++ templates constructor shared-ptr

我目前可以初始化以下课程MyTest

template<class T>
class MyTest {

public:
    typedef std::shared_ptr<MyTest> Ptr;

    MyTest( Ptr _nextTest = NULL ) : m_nextTest( _nextTest ) { }
    ~MyTest() { }

private:
    Ptr m_nextTest;

};

喜欢这样

MyTest<int>::Ptr my( new MyTest<int>() );

我现在也希望能够有一个MyTest不同的类型作为构造函数的参数,如

MyTest<int>::Ptr my( new MyTest<float>() );

但是这显然是制动(* 1),因为Ptr中的MyTest的typedef仅限于一种类型。有没有办法实现这一点,而无需为MyTest的每个实例手动指定第二种类型?


编辑:(建议发表评论以详细说明问题) 我的目标是建立某种管道。管道将由几个独立的部分组成。我想以固有的方式控制这些碎片可以插入另一个碎片的方式。这意味着,如果当前作品具有int输入和float输出,则下一部分必须具有float作为输入。我做到了这一点,但你可以看到我对模板的了解相当有限:

template<class I, class O>
class AbstractPipelineTask {

public:
    typedef std::shared_ptr<AbstractPipelineTask> Ptr;

    AbstractPipelineTask( Ptr _nextTask ) : m_nextTask ( _nextTask ) { }
    virtual ~AbstractPipelineTask() { }

    void call( I _input ) {

        m_input = _input;
        m_output = executeTask( m_input );

        if ( m_nextTask )
            m_nextTask->call( m_output );
    }

    virtual O executeTask( I _input ) = 0;

protected:  
    Ptr m_nextTask;

    I m_input;
    O m_output;
};


template<class I, class O>
class ActualPipelineTask : public AbstractPipelineTask< I, O > {

public:
    typedef std::shared_ptr<ActualPipelineTask> Ptr;

    ActualPipelineTask( AbstractPipelineTask<I,O>::Ptr _nextTask ) : AbstractPipelineTask( _nextTask ) { }
    virtual ~ActualPipelineTask() { }

    virtual O executeTask( I _input ) {

        return ( _input * _input );
    }
};

class Str2IntPipelineTask : public AbstractPipelineTask< std::string, int > {

public:
    typedef std::shared_ptr<Str2IntPipelineTask> Ptr;

    Str2IntPipelineTask( AbstractPipelineTask<std::string,int>::Ptr _nextTask = NULL ) : AbstractPipelineTask( _nextTask ) { }
    virtual ~Str2IntPipelineTask() { }

    virtual int executeTask( std::string _input ) {

        return atoi( _input.c_str() );
    }
};

我可以根据需要将尽可能多的ActualPipelineTasks插入一起,但我无法将任何不具有完全相同IO类型的内容组合在一起:

// Works
ActualPipelineTask<int,int>::Ptr    pipeTask2( new ActualPipelineTask<int,int>() );
ActualPipelineTask<int,int>::Ptr    pipeTask1( pipeTask2 );

// Doesn't work (*2)
ActualPipelineTask<int,int>::Ptr    pipeTask2( new ActualPipelineTask<int,int>() );
ActualPipelineTask<int,int>::Ptr    pipeTask1( pipeTask2 );
Str2IntPipelineTask::Ptr            pipeTask0( pipeTask1 );

我可能使用错误的工具来实现我想要的目标。但那是我唯一能想到的。

* 1:

  

错误C2664:'std :: _ Ptr_base&lt; _Ty&gt; :: _ Reset0':无法将参数1从'MyTest *'转换为'MyTest *'

* 2

  

错误C2664:'std :: shared_ptr&lt; _Ty&gt; :: shared_ptr(std :: nullptr_t)':无法从'std :: shared_ptr&lt; _Ty&gt;'转换参数1到'std :: nullptr_t'

1 个答案:

答案 0 :(得分:2)

MyTest<int>MyTest<float>完全不相关的类型。无论涉及任何typedef,指向其中一种类型的指针(智能或普通)都不能指向另一种类型的对象。

如果您希望能够指向能够指向两者的指针,则必须重新设计MyTest类模板 - 可能是从非模板基类派生的。但请记住,这确实是一个重新设计 - 您可能需要在此基类中使用虚拟接口,依此类推。这样解决问题。没有简单的黑客可用。


关于您的更新。

始终牢记模板是一个编译时构造。在编译时,您可以在使用模板时使用模板。在你的情况下,情况并非如此。

您已将输入类型和输出类型合并到任务类型中。因此,在构建管道时,管道中的每个任务都必须知道输入类型下一个任务的输出类型。

如何解决这个问题取决于您打算如何使用管道。有两种可能的情况:

场景A。您在编译时知道管道的结构(类型序列),并希望能够在运行时选择任务。您可以通过分离&#34;排序&#34;来完成当前类的操作。来自&#34;执行的功能&#34;功能(无论如何这都是一个好主意:一个班级应该只有一个责任)。代码将如下所示:

template<class I, class O>
class AbstractPipelineTask {

public:
    typedef std::shared_ptr<AbstractPipelineTask> Ptr;

    virtual ~AbstractPipelineTask() { }

    virtual O executeTask( I _input ) = 0;

protected:  
    Ptr m_nextTask;
};


class MyKnownPipeline
{
  AbstractPipelineTask<int, int>::Ptr task0;
  AbstractPipelineTask<int, std::string>::Ptr task1;
  AbstractPipelineTask<std::string, int>::Ptr task2;

public:
  // Functions to set up the tasks somehow

  int call(int input)
  {
    return task2->execute(task1->execute(task0->execute(input)));
  }
};

为了使其更加灵活(例如,如果您有多个管道),可以将MyKnownPipeline转换为可变参数类模板。我认为这样做超出了这个答案的范围,它将是相当复杂的模板代码。

场景B。您在编译时不知道管道的结构。您只知道您将拥有一个管道并在运行时以某种方式配置它(或仅使用运行时信息)。

在这种情况下,模板无法真正帮助您(至少在界面部分)。您需要抽象基类与类型无关。研究如何进行类型擦除并将未知类型的数据存储在内存中(参见例如Boost.VariantBoost.Any)。

您仍然可以在实施方面使用模板。这里有一个关于可以做什么的想法;我并不保证以这种方式完成,正确或明智。

class AbstractTask
{
public:
  virtual bool acceptableNext(const AbstractTask &next) const = 0;

  virtual boost::any execute(boost::any input) = 0;
};


template <class I>
class KnownInputTask : public AbstractTask
{
public:
  boost::any execute(boost::any input) override final
  {
    return execute_impl(any_cast<I>(input));
  }

private:
  virtual boost::any execute_impl(I) = 0;
};


template <class I, class O>
class KnownInOutTask : KnownInputTask<I>
{
public:
  bool acceptableNext(const AbstractTask &next) const override
  {
    return dynamic_cast<KnownInputTask<I>*>(&next) != nullptr;
  };
};


class Pipeline
{
  std::vector<AbstactTask::Ptr> tasks;

public:
  bool addTask(AbstractTask::Ptr task)
  {
    if (!tasks.empty() && !tasks.back()->acceptableNext(*task)) return false;
    tasks.push_back(task);
    return true;
  }

  boost::any call(boost::any input) const
  {
    for (auto t : tasks)
      input = t->execute(input);
    return input;
  }
};