C++ - 定义类模板(头文件/源文件)
C++ - Defining class template (header/source file)
我想在voreen中创建一个处理器(就像这个.cpp|.h)移植这个OTB应用程序:
http://hg.orfeo-toolbox.org/OTB/file/ca4366bb972e/Applications/Segmentation/otbSegmentation.cxx
我已经将所有参数编码到属性等中,但是..
如果你看一下376,你会看到一个类模板FloatVectorImageType::SizeType,一个typedef类型。
我不熟悉 c++ 模板,所以我的第一个问题是我应该把这个模板的实现放在处理器的 .cpp 或 .h 文件中的什么地方?简要看一下 c++ 教程和其他处理器示例,如上面所示,我发现我必须在标头中声明模板并在.cpp中定义它。
问题是编译器不允许我在.cpp中定义 typedef 类型的模板类。类型定义无法识别。
那么,有人可以在这里指出我正确的方向吗?
分段处理器.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
分段处理器.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
错误:"矢量图像类型"未命名类型(已修复)
我不熟悉 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;
这将实例化类模板my_type
一次用于模板参数int
,一次实例化模板参数double
。这将创建两个具有相似名称的独立且不相关的类型:my_type<int>
和 my_type<double>
函数也是如此;除了函数之外,通常不会显式提供模板参数。相反,您可以让编译器从函数参数的类型中推断模板参数。例:
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<int>(int)
(名称为 foo<int>
,并且具有单个类型为 int
的函数参数)和foo<double>(double)
为什么
将模板的定义放在源文件中是不好的:请参阅为什么模板只能在头文件中实现?
简短版本:由于模板是蓝图,为了使用它们,编译器必须实例化它们。但它只能实例化它所知道的。
如果在头文件templ.h
中声明函数模板foo
,则在 templ.cpp
中定义并在 main.cpp
中使用它,则:
在
main.cpp
中,编译器不知道函数模板的定义。它只能实例化foo
的声明,而不能实例化定义。在
templ.cpp
中,编译器确实知道定义,并且可以实例化它。但是,它不知道templ.cpp
外部的用途 - 因此它无法为外部使用的所有参数集实例化它。
在 OP 的示例中,这有效,但这似乎是一个疏忽:
[圣殿]
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>
}
[主要.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<char>
的定义没有在templ.cpp
中实例化,也不能在main.cpp
中实例化,这就产生了链接器错误。
这就是为什么你不应该依赖这种行为。如果出于某种原因不想在头文件中定义函数模板,则可以使用显式实例化。至少,显式实例化是显式的,应该记录下来,这样就不会发生意外。
编译器实际抱怨的问题与模板无关;)这只是一个名称查找问题。一个简化的示例:
#include <iostream>
struct Foo
{
typedef int Bar;
void do_something();
};
Bar Foo::do_something()
{
std::cout << "somethingn";
}
当编译器看到该行时Bar Foo::do_something()
它看到的是Bar
,并且找不到该名称所指的内容。因此错误。另一方面:
#include <iostream>
struct Foo
{
typedef int Bar;
void do_something();
};
Foo::Bar Foo::do_something()
{
std::cout << "somethingn";
}
现在你告诉编译器在哪里查找名称Bar
,即在Foo
里面。
以下是我为了帮助您而编写的模板的简短示例:
在标题中:
typedef TemplatedClassName< ParameterValue0, ParameterValue1 > TemplateAlias;
在源文件中:
显式模板实例化
template class TemplatedClassName< ParameterValue0, ParameterValue1 >;
- 为测试目标创建具有不同源文件夹的文件
- 生成一个生成文件,该生成文件使用Automake在一个步骤中编译和链接所有源文件
- 生成文件:动态源文件名和对象目录
- 无法使用 CMake 从其他文件夹添加源文件
- CMake 源文件找不到头文件
- 给定一个源文件,有没有办法要求 gcc 返回仅直接包含的头文件的列表?
- UE4 - Visual Studio在我从编辑器添加新的c ++文件后无法打开任何源文件 - UBT_COMPILED
- cpp 在主源文件中包括.cpp文件导致错误"duplicate symbol"
- 如何将 .ui 完全转换为 C++ 头文件和源文件
- 在生成文件中添加源文件并更新依赖项
- CMake 在源文件附近找不到头文件
- GNU 在看到其他同名文件时重用源文件
- #include "date.h" 创建错误 E1696 无法打开源文件"date.h",也无法打开包含文件:没有这样的文件或目录
- DirectX 11 文件无法打开源文件
- 用libclang解析源文件 - 链接问题包括文件
- C++ - 定义类模板(头文件/源文件)
- 在使用头文件/源文件时访问类变量的麻烦
- 如何声明和定义全局变量,以便从所有头文件/源文件中正确访问它们
- 将lambda传递给模板类中的方法(头文件/源文件问题)
- LNK 2019/1120我的头文件/源文件实践中的双重链接列表错误