C ++ - 定义类模板(头/源文件) [英] C++ - Defining class template (header/source file)

查看:256
本文介绍了C ++ - 定义类模板(头/源文件)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在voreen中创建处理器(像这个 .cpp a> | .h )移植此OTB应用程序:



http ://hg.orfeo-toolbox.org/OTB/file/ca4366bb972e/Applications/Segmentation/otbSegmentation.cxx



我已编码了几乎所有参数into属性等。



如果你看看 376 ,您将看到一个类模板FloatVectorImageType :: SizeType,一个typedef类型。



我不熟悉c ++模板,所以我的第一个问题是我应该把这个模板的实现,在处理器的.cpp或.h文件中的哪里?简要介绍c ++教程和其他处理器示例,如上述 ,我想出我必须声明模板在头文件并定义它在.cpp。



问题是,编译器不允许我定义一个模板类.cpp中的typedef类型。无法识别typedef。



那么,有人可以指点我的方向吗?



> segmentationprocessor.h

  #ifndef OTBSEGMENTATIONAPPLICATION_H 
#define OTBSEGMENTATIONAPPLICATION_H

#includeotbVectorImage.h
#includemodules / otb / ports / otbimageport.h
#includemodules / otb / ports / otbvectorimageport.h
#includevoreen / core / properties / boolproperty.h

//其中包括

命名空间voreen {

OTBSegmentationApplication:public OTBImageFilterProcessor
{
public:
OTBSegmentationApplication();

virtual〜OTBSegmentationApplication();

virtual Processor * create()const;

virtual std :: string getCategory()const {returnApplications; }
virtual std :: string getClassName()const {returnSegmentation Application; }
virtual CodeState getCodeState()const {return CODE_STATE_EXPERIMENTAL;} // STABLE,TESTING,EXPERIMENTAL

virtual std :: string getProcessorInfo()const;

/ **图片typedefs * /
typedef otb :: VectorImage< double,2> VectorImageType;
typedef ImageType LabelImageType;
typedef ImageType MaskImageType;

typedef VectorImageType :: SizeType size;

//分割过滤器typedefs
// Edison mean-shift
typedef otb :: MeanShiftVectorImageFilter< VectorImageType,VectorImageType,LabelImageType> EdisonSegmentationFilterType;
EdisonSegmentationFilterType :: Pointer edisonFilter;

// Home made mean-shift
typedef otb :: MeanShiftSegmentationFilter< VectorImageType,LabelImageType,VectorImageType> MeanShiftSegmentationFilterType;
MeanShiftSegmentationFilterType :: Pointer meanshiftFilter;

//简单连接的组件
typedef otb :: Functor :: ConnectedComponentMuParserFunctor< VectorImageType :: PixelType> FunctorType;

typedef itk :: ConnectedComponentFunctorImageFilter< VectorImageType,LabelImageType,FunctorType,MaskImageType> ConnectedComponentSegmentationFilterType;
ConnectedComponentSegmentationFilterType :: Pointer ccFilter;

typedef itk :: ScalarConnectedComponentImageFilter< LabelImageType,LabelImageType> LabeledConnectedComponentSegmentationFilterType;
LabeledConnectedComponentSegmentationFilterType :: Pointer labeledCCFilter;

//..more typedefs here

protected:
virtual void setDescriptions(){
setDescription(执行图像的分割和输出在矢量模式下,支持大输入数据集。
}
void process();
virtual void initialize()throw(tgt :: Exception);
virtual void deinitialize()throw(tgt :: Exception);

/ ** TEMPLATE DECLARATION(?)* /

template< class TInputImage,class TSegmentationFilter>
VectorImageType :: SizeType GenericApplySegmentation(otb :: StreamingImageToOGRLayerSegmentationFilter
< TInputImage,TSegmentationFilter> * streamingVectorizedFilter,TInputImage * inputImage,
const otb :: ogr :: Layer& layer,const unsigned int outputNb );
virtual void updateFilterSelection();
virtual void updateModeSelection();

private:

OTBVectorImagePort inPort_;
StringOptionProperty filter_; ///<选择分割算法
OTBVectorImagePort vectorOutPort_;
OTBImagePort vectorMaskInPort_;
OTBImagePort outPort_;

//..more这里的属性定义

static const std :: string loggerCat_; ///<日志中使用的类别
};

} //命名空间

#endif // OTBSEGMENTATIONAPPLICATION_H


$ b b

segmentationprocessor.cpp

  #includesegmentationprocessor。 h
#includevoreen / core / voreenapplication.h

namespace voreen {

const std :: string OTBSegmentationApplication :: loggerCat _(voreen.OTBSegmentationApplication );

OTBSegmentationApplication :: OTBSegmentationApplication()
:OTBImageFilterProcessor(),
inPort_(Port :: INPORT,IN Multiband Image,0),
vectorOutPort_ :: OUTPORT,OUT Multiband Image,0),
vectorMaskInPort_(Port :: INPORT,IN Mask Image,0),
outPort_(Port :: OUTPORT,OUT OTB Image 0),

filter _(selectFilter,Segmentation algorithm),

// ..这里有更多的属性代码

{
addPort(inPort_);
addPort(vectorOutPort_);
addPort(vectorMaskInPort_);
addPort(outPort_);

addProperty(filter_);

// ..添加其余的属性

edisonFilter = EdisonSegmentationFilterType :: New();
meanshiftFilter = MeanShiftSegmentationFilterType :: New();
ccFilter = ConnectedComponentSegmentationFilterType :: New();

// ...在这里实现所需的过滤器

}

处理器* OTBSegmentationApplication :: create()const {
return new OTBSegmentationApplication();
}

OTBSegmentationApplication ::〜OTBSegmentationApplication(){

}

无效OTBSegmentationApplication ::初始化()掷(TGT ::异常){
Processor :: initialize();
}

void OTBSegmentationApplication :: deinitialize()throw(tgt :: Exception){
Processor :: deinitialize();
}

std :: string OTBSegmentationApplication :: getProcessorInfo()const {
returnSegmentation Application;
}

无效OTBSegmentationApplication :: updateFilterSelection(){
//对性能的视觉更新代码在这里
}

无效OTBSegmentationApplication: :(?)updateModeSelection(){$关于物业视觉更新b $ b //代码在这里
}

//模板实现HERE
模板<类TInputImage,类TSegmentationFilter> ;
OTBSegmentationApplication :: VectorImageType :: SizeType OTBSegmentationApplication :: GenericApplySegmentation(OTB :: StreamingImageToOGRLayerSegmentationFilter< TInputImage,
TSegmentationFilter> * streamingVectorizedFilter,TInputImage * inputImage的常量OTB :: OGR ::层和放大器;层,const的无符号整型outputNb)
{
typedef TSegmentationFilter SegmentationFilterType;
typedef typename SegmentationFilterType :: Pointer SegmentationFilterPointerType;
typedef otb :: StreamingImageToOGRLayerSegmentationFilter< TInputImage,SegmentationFilterType> StreamingVectorizedSegmentationOGRType;

//..其余的模板代码在这里
}


void OTBSegmentationApplication :: process(){

try
{
//处理器实现在这里
LINFO(Segmentation Application Connected);
}
catch(int e)
{
LERROR(Segmentation Applicationn中的错误);
return;
}
}

} //命名空间

错误:'VectorImageType'未命名类型(固定)



解决方案

我不熟悉C ++模板,所以我的第一个问题是,我应该把这个模板的实现,在处理器的.cpp或.h文件中?


将其放在头文件中。这是最简单和最强大的解决方案。通常,您要将函数(即其函数体)的定义放在源文件(.cpp)中,因为源文件可以独立编译。 但这不适用于模板(*)





类模板只是类的蓝图,函数模板是函数的蓝图。也就是说,的函数模板不是函数的,换句话说,模板函数是一种误导,它不是一个功能,而是一个模板/蓝图。



从函数模板(或类模板中的类)构建函数的过程称为实例化 。其结果是一个实例化的功能,或者更一般地说,一个函数模板的专业化



模板特是的没有模板。函数模板专门化只是一个具有奇怪名称的普通函数; 。类模板特化仅仅是一个怪异的名称类



一个模板将只被实例化模板参数一些特定集合:




  • 要么要求它实例化

  • 或者如果你只是使用特殊化> implicit 实例化)。



第二种方法是更常见的。示例:

 模板< class T& 
struct my_type
{
T mem;
};

//使用specialization - >隐式实例化
my_type< int> o;
my_type< double> p;

这将实例化类模板 my_type 一次用于模板参数 int ,一次用于模板参数 double 。这会创建两个类似名称的独立无关的类型: my_type< int> my_type< ; double>



但对于函数,您通常不显式提供模板参数。相反,你让编译器从函数参数的类型中推导出模板参数。示例:

 模板< class T& 
void foo(T param)
{
std :: cout<帕拉姆
}

foo< int>(42); //明确指定模板参数 - 不要这样
foo(21); // template argument * deduction *

第二次调用会自动推断模板参数为 int 。再次,我们创建了(隐式实例化)具有类似名称的两个函数: foo< int>(int)(名称为 foo< int& / code>,并且它具有 int 类型的单个函数参数和 foo< double>(double)






为什么将模板的定义放在源文件中很不好:参见为什么只能在头文件中实现模板?



短版本:由于模板是蓝图,为了使用它们,编译器必须实例化它们。但它只能实例化



如果你声明一个函数模板 foo 在头文件 templ.h 中定义,并在 templ.cpp 中定义,并在 main.cpp ,然后:




  • / code>,编译器不知道函数模板的定义。它只能实例化 foo 声明,但不能定义。


  • p>在 templ.cpp 中,编译器知道定义,并且可以实例化它。但是,它不知道 templ.cpp 之外的用法 - 因此它不能实例化外部使用的所有参数集。




在OP中的示例中,它起作用,但似乎是一个疏忽:



[templ.h]

 模板< class T> 
void foo(T);

void ordinary_function();

[templ.cpp]

  #includetempl.h

template< class T>
void foo(T p)
{
std :: cout< p;
}

void ordinary_function()
{
foo(42); // foo的隐式实例化< int>
foo(2.5); // foo< double>的隐式实例化
}

[main.cpp]


$ b b

  #includetempl.h
int main()
{
foo(23); //工作正常,使用foo< int>在templ.cpp中定义
foo('a'); // linker error:foo< char>未定义
return 0;
}

因为 foo< char> 尚未在 templ.cpp 中实例化,并且不能在 main.cpp ,会产生一个链接器错误。



这就是为什么你不应该依赖这种行为。如果由于某种原因不能 想在头文件中定义函数模板,则可以使用显式实例化。至少,显式实例化是显式的,并且应该被记录,以便没有惊喜发生。






实际上抱怨与模板无关);这只是一个名称查找问题。一个简化的例子:

  #include< iostream> 

struct Foo
{
typedef int Bar;

void do_something();
};

Bar Foo :: do_something()
{
std :: cout< something\\\
;
}

当编译器看到 Bar Foo :: do_something()它看到 Bar ,并且找不到该名称所指的。因此错误。另一方面:

  #include< iostream> 

struct Foo
{
typedef int Bar;

void do_something();
};

Foo :: Bar Foo :: do_something()
{
std :: cout< something\\\
;
}



现在你告诉编译器在哪里找到 Bar ,即 Foo 内。


I want to create a processor in voreen (like this one .cpp | .h) porting this OTB-Application:

http://hg.orfeo-toolbox.org/OTB/file/ca4366bb972e/Applications/Segmentation/otbSegmentation.cxx

I have coded almost all the parameters into properties, etc but..

If you look at like 376, you'll see a class template of FloatVectorImageType::SizeType, a typedef type.

Im not familiar with c++ templates so my first question was where should I put this template's implementation, in the .cpp or .h file of the processor? Taking a brief look at c++ tutorials and other processor examples like the one above, I figure out I have to declare the template in header and define it in the .cpp.

The problem is that the compiler doesnt allow me to define a template class of a typedef type inside the .cpp. The typedef isnt recognised..

So, can someone point me to the right direction here?

segmentationprocessor.h

#ifndef OTBSEGMENTATIONAPPLICATION_H
#define OTBSEGMENTATIONAPPLICATION_H

#include "otbVectorImage.h"
#include "modules/otb/ports/otbimageport.h"
#include "modules/otb/ports/otbvectorimageport.h"
#include "voreen/core/properties/boolproperty.h"

//..more includes here

namespace voreen {

class OTBSegmentationApplication : public OTBImageFilterProcessor
{
public:
    OTBSegmentationApplication();

    virtual ~OTBSegmentationApplication();

    virtual Processor* create() const;

    virtual std::string getCategory() const { return "Applications"; }
    virtual std::string getClassName() const { return "Segmentation Application"; }
    virtual CodeState getCodeState() const { return CODE_STATE_EXPERIMENTAL;}//STABLE, TESTING, EXPERIMENTAL

    virtual std::string getProcessorInfo() const;

    /** Images typedefs */
    typedef otb::VectorImage<double, 2> VectorImageType;
    typedef ImageType               LabelImageType;
    typedef ImageType               MaskImageType;

    typedef VectorImageType::SizeType size;

    // Segmentation filters typedefs
    // Edison mean-shift
    typedef otb::MeanShiftVectorImageFilter<VectorImageType,VectorImageType,LabelImageType> EdisonSegmentationFilterType;
    EdisonSegmentationFilterType::Pointer edisonFilter;

    // Home made mean-shift
    typedef otb::MeanShiftSegmentationFilter<VectorImageType, LabelImageType, VectorImageType> MeanShiftSegmentationFilterType;
    MeanShiftSegmentationFilterType::Pointer meanshiftFilter;

    // Simple connected components
    typedef otb::Functor::ConnectedComponentMuParserFunctor<VectorImageType::PixelType> FunctorType;

    typedef itk::ConnectedComponentFunctorImageFilter <VectorImageType, LabelImageType, FunctorType, MaskImageType> ConnectedComponentSegmentationFilterType;
    ConnectedComponentSegmentationFilterType::Pointer ccFilter;

    typedef itk::ScalarConnectedComponentImageFilter<LabelImageType, LabelImageType> LabeledConnectedComponentSegmentationFilterType;
    LabeledConnectedComponentSegmentationFilterType::Pointer labeledCCFilter;

    //..more typedefs here

protected:
    virtual void setDescriptions() {
        setDescription("Performs segmentation of an image, and output either a raster or a vector file. In vector mode, large input datasets are supported.");
    }
    void process();
    virtual void initialize() throw (tgt::Exception);
    virtual void deinitialize() throw (tgt::Exception);

    /** TEMPLATE DECLARATION (?) */

    template<class TInputImage, class TSegmentationFilter>
    VectorImageType::SizeType GenericApplySegmentation(otb::StreamingImageToOGRLayerSegmentationFilter
                                                       <TInputImage, TSegmentationFilter> * streamingVectorizedFilter, TInputImage * inputImage,
                                                       const otb::ogr::Layer& layer, const unsigned int outputNb);
    virtual void updateFilterSelection();
    virtual void updateModeSelection();

private:

    OTBVectorImagePort inPort_;
    StringOptionProperty filter_; ///< Select segmentation algorithm
    OTBVectorImagePort vectorOutPort_;
    OTBImagePort vectorMaskInPort_;
    OTBImagePort outPort_;

    //..more property definitions here

    static const std::string loggerCat_; ///< Category used in logging
};

} // namespace

#endif // OTBSEGMENTATIONAPPLICATION_H

segmentationprocessor.cpp

#include "segmentationprocessor.h"
#include "voreen/core/voreenapplication.h"

namespace voreen {

const std::string OTBSegmentationApplication::loggerCat_("voreen.OTBSegmentationApplication");

OTBSegmentationApplication::OTBSegmentationApplication()
    :OTBImageFilterProcessor(),
      inPort_(Port::INPORT, "IN Multiband Image", 0),
      vectorOutPort_(Port::OUTPORT, "OUT Multiband Image", 0),
      vectorMaskInPort_(Port::INPORT, "IN Mask Image", 0),
      outPort_(Port::OUTPORT, "OUT OTB Image", 0),

      filter_("selectFilter", "Segmentation algorithm"),

      //.. more properties code here

{
    addPort(inPort_);
    addPort(vectorOutPort_);
    addPort(vectorMaskInPort_);
    addPort(outPort_);

    addProperty(filter_);

    //.. adding the rest of properties here

    edisonFilter = EdisonSegmentationFilterType::New();
    meanshiftFilter = MeanShiftSegmentationFilterType::New();
    ccFilter = ConnectedComponentSegmentationFilterType::New();

    //..instantiating more filters needed in implementation here

}

Processor* OTBSegmentationApplication::create() const {
    return new OTBSegmentationApplication();
}

OTBSegmentationApplication::~OTBSegmentationApplication() {

}

void OTBSegmentationApplication::initialize() throw (tgt::Exception) {
    Processor::initialize();
}

void OTBSegmentationApplication::deinitialize() throw (tgt::Exception) {
    Processor::deinitialize();
}

std::string OTBSegmentationApplication::getProcessorInfo() const {
    return "Segmentation Application";
}

void OTBSegmentationApplication::updateFilterSelection() {
    //code for visual updates on properties here
}

void OTBSegmentationApplication::updateModeSelection() {
    //code for visual updates on properties here
}

    //TEMPLATE IMPLEMENTATION HERE (?)
template<class TInputImage, class TSegmentationFilter>
OTBSegmentationApplication::VectorImageType::SizeType OTBSegmentationApplication::GenericApplySegmentation(otb::StreamingImageToOGRLayerSegmentationFilter<TInputImage,
                             TSegmentationFilter> * streamingVectorizedFilter, TInputImage * inputImage, const otb::ogr::Layer& layer, const unsigned int outputNb)
    {
        typedef  TSegmentationFilter SegmentationFilterType;
        typedef  typename SegmentationFilterType::Pointer SegmentationFilterPointerType;
        typedef otb::StreamingImageToOGRLayerSegmentationFilter<TInputImage,SegmentationFilterType> StreamingVectorizedSegmentationOGRType;

    //..the rest of template code here
}


void OTBSegmentationApplication::process() {

    try
    {
        //PROCESSOR IMPLEMENTATION GOES HERE
        LINFO("Segmentation Application Connected");
    }
    catch (int e)
    {
        LERROR("Error in Segmentation Applicationn");
        return;
    }
}

}   // namespace

error: ‘VectorImageType’ does not name a type (fixed)

解决方案

I'm not familiar with c++ templates so my first question was where should I put this template's implementation, in the .cpp or .h file of the processor?

Put it in the header file. That's the simplest and most robust solution. Typically, you want to put definitions of functions (i.e. their function body) in source files (.cpp), as source files can be compiled independently. But this isn't possible for templates(*).

(*) slightly simplified.

Class templates are just blueprints for classes, function templates are blueprints for functions. That is, function templates are not functions, in other words "template function" is misleading, it's not a function but a template/blueprint.

The process of building a function from a function template (or class from a class template) is called instantiation. The result is an instantiated function, or, more generally, a function template specialization.

Template specializations are no templates. A function template specialization is just an ordinary function with a weird name; a class template specialization is just a class with a weird name.

A template will only be instantiated for some specific sets of template arguments:

  • Either if you explicitly ask it to be instantiated
  • or if you just use a specialization (-> implicit instantiation).

The second way is by far the more common. An example:

template<class T>
struct my_type
{
    T mem;
};

// using the specialization -> implicit instantiation
my_type<int> o;
my_type<double> p;

This will instantiate the class template my_type once for the template argument int, and once for the template argument double. This creates two independent and unrelated types with similar names: my_type<int> and my_type<double>

And similarly for functions; except that for functions, you typically do not explicitly provide the template arguments. Instead, you let the compiler deduce the template arguments from the types of the function arguments. Example:

template<class T>
void foo(T param)
{
    std::cout << param;
}

foo<int>(42);  // explicitly specifying the template arguments -- DON'T DO THAT
foo(21);       // template argument *deduction*

The second call will automatically deduce the template argument to be int. Again, we have created (implicitly instantiated) two functions with similar names: foo<int>(int) (the name is foo<int> and it has a single function parameter of type int) and foo<double>(double)


Why it's bad to put the definitions of templates in a source file: See Why can templates only be implemented in the header file?

Short version: As templates are blueprints, in order to use them, the compiler has to instantiate them. But it can only instantiate what it knows.

If you declare a function template foo in a header file templ.h, define it in templ.cpp and use it in main.cpp, then:

  • In main.cpp, the compiler doesn't know about the definition of the function template. It can only instantiate the declaration of foo, but not the definition.

  • In templ.cpp, the compiler does know about the definition, and can instantiate it. However, it doesn't know about the uses outside templ.cpp -- therefore it cannot instantiate it for all sets of arguments used outside.

In the example in the OP, that works, but it seems to be an oversight:

[templ.h]

template<class T>
void foo(T);

void ordinary_function();

[templ.cpp]

#include "templ.h"

template<class T>
void foo(T p)
{
    std::cout << p;
}

void ordinary_function()
{
    foo(42);     // implicit instantiation of foo<int>
    foo(2.5);    // implicit instantiation of foo<double>
}

[main.cpp]

#include "templ.h"
int main()
{
    foo(23);    // works fine, uses the foo<int> defined in templ.cpp
    foo('a');   // linker error: foo<char> not defined
    return 0;
}

Because the definition of foo<char> hasn't been instantiated in templ.cpp, and it cannot be instantiated in main.cpp, this produces a linker error.

That's why you should NOT rely on this behaviour. You can use explicit instantiation if you, for some reason, do not want to define the function template in the header file. At least, the explicit instantiation is explicit, and should be documented, so that no surprises happen.


The problem the compiler is actually complaining about has nothing to do with templates ;) It's just a name lookup issue. A simplified example:

#include <iostream>

struct Foo
{
    typedef int Bar;

    void do_something();
};

Bar Foo::do_something()
{
    std::cout << "something\n";
}

When the compiler sees the line Bar Foo::do_something() it sees Bar, and cannot find what that name refers to. Hence the error. On the other hand:

#include <iostream>

struct Foo
{
    typedef int Bar;

    void do_something();
};

Foo::Bar Foo::do_something()
{
    std::cout << "something\n";
}

Now you told the compiler where to look for the name Bar, namely, inside Foo.

这篇关于C ++ - 定义类模板(头/源文件)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆