我目前可以初始化以下课程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
插入一起,但我无法将任何不具有完全相同I
和O
类型的内容组合在一起:
// 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'
答案 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.Variant或Boost.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;
}
};