【问题标题】:C++ - Defining class template (header/source file)C++ - 定义类模板(头文件/源文件)
【发布时间】:2013-11-12 10:09:38
【问题描述】:

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

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

我已经将几乎所有的参数都编码成属性等,但是..

如果您查看376,您将看到 FloatVectorImageType::SizeType 的类模板,这是一个 typedef 类型。

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

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

那么,有人可以在这里指出正确的方向吗?

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

错误:“VectorImageType”没有命名类型(固定)

【问题讨论】:

  • Why can templates only be implemented in the header file?;请将任何相关代码复制到问题中。这样可以更轻松地理解您的问题并防止出现链接断开的问题(将来)。
  • 这是我检查的第一个教程,谢谢。我可以将代码复制到问题中,但恐怕它有点大..
  • 只有 相关 部分;) 或者更好的 a short, but complete example 重现或说明您的问题
  • CurvatureAnisotropicDiffusionImageFilterITK::curvatureAnisotropicDiffusionImageFilterITK 的情况当然很奇怪。它是受保护的,但在 .cpp 中定义了这意味着,任何派生类只能使用在 .cpp 中生成的实例化,这些实例化用于 floatdouble 类型——这些是隐式 实例化。这似乎不是故意的。
  • VectorImageType::SizeType OTBSegmentationApplication::GenericApplySegmentation 试试OTBSegmentationApplication::VectorImageType::SizeType OTBSegmentationApplication::GenericApplySegmentation 我会写一个涵盖其他问题的答案,但这需要一些时间。

标签: c++ header-files porting class-template


【解决方案1】:

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

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

(*) 略微简化。

类模板只是类的蓝图,函数模板是函数的蓝图。也就是说,函数模板不是函数,换句话说,“模板函数”具有误导性,它不是函数而是模板/蓝图。

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

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

模板只会针对某些特定的模板参数集进行实例化:

  • 如果您明确要求实例化它
  • 或者如果您只使用特殊化(-> 隐式 实例化)。

到目前为止,第二种方式更为常见。一个例子:

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

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

这将为模板参数int 实例化一次类模板 my_type,并为模板参数double 实例化一次。这将创建两个具有相似名称的 独立不相关 类型:my_type&lt;int&gt;my_type&lt;double&gt;

对于函数也是如此;除了函数,您通常不显式提供模板参数。相反,您让编译器从函数参数的类型中推断出模板参数。示例:

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*

第二次调用会自动推断模板参数为int。同样,我们创建(隐式实例化)两个名称相似的函数:foo&lt;int&gt;(int)(名称为 foo&lt;int&gt;,它有一个类型为 int 的函数参数)和 foo&lt;double&gt;(double)


为什么将模板的定义放在源文件中是不好的:参见Why can templates only be implemented in the header file?

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

如果你在头文件templ.h中声明一个函数模板foo,在templ.cpp中定义它并在main.cpp中使用它,那么:

  • main.cpp 中,编译器不知道函数模板的定义。它只能实例化foo声明,不能实例化定义。

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

在 OP 中的示例中,这是可行的,但它似乎是一个疏忽:

[模板.h]

template<class T>
void foo(T);

void ordinary_function();

[模板.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;
}

因为foo&lt;char&gt;定义templ.cpp中没有被实例化,也无法在main.cpp中被实例化,所以会产生链接器错误。

这就是为什么你不应该依赖这种行为。如果您出于某种原因想要在头文件中定义函数模板,则可以使用显式实例化。至少,显式实例化是显式的,并且应该记录在案,以免发生意外。


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

#include <iostream>

struct Foo
{
    typedef int Bar;

    void do_something();
};

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

当编译器看到Bar Foo::do_something() 行时,它会看到Bar,但找不到该名称所指的内容。因此错误。另一方面:

#include <iostream>

struct Foo
{
    typedef int Bar;

    void do_something();
};

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

现在您告诉编译器在哪里查找名称 Bar,即在 Foo 内部。

【讨论】:

  • 非常感谢您的详细解释!我不得不承认我花了一些时间来理解这两种方法之间的区别。来自 voreen/ITK 的例子让我很困惑,因为就像我的情况一样,正如你所说,它们可以以任何方式工作。我尽量听从你的建议.. :)
  • @cchantep “函数”这个词是对的,句子下面的例子显示了从函数调用中推断出类型quimnuss 澄清了,我希望。此外,第一个&gt; 引入了来自 OP 的引用。这也是故意的。
  • 是的,我意识到这是对的,但被写作误导了,所以我澄清了它。
【解决方案2】:

这是我为帮助您而编写的关于模板的简短示例:
在标题中:

typedef TemplatedClassName< ParameterValue0, ParameterValue1 > TemplateAlias;

在源文件中:
// 显式模板实例化

template class TemplatedClassName< ParameterValue0, ParameterValue1 >;

【讨论】:

    猜你喜欢
    • 2015-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-08
    • 1970-01-01
    • 2012-04-03
    • 1970-01-01
    相关资源
    最近更新 更多