当模板类型的行为相同时,定义模板化类的成员函数的正确方法是什么?

What is the proper way to define a templated class's member function when behavior is identical for template types?

本文关键字:函数 成员 是什么 方法 类型 定义      更新时间:2023-10-16

考虑到如果没有破坏,我会破坏它,我决定专门化一个类,以便它可以在float和双精度之间自动模板化。

我有下面的[简化]类声明:

// Quatcam.h
#pragma once
#include <boost/math/quaternion.hpp>
#include <boost/numeric/ublas/matrix.hpp>
template<typename FloatType>
class QuaternionCamera
{
public:
    QuaternionCamera();
    void applyTranslation(boost::numeric::ublas::vector<FloatType> translationVector);
    boost::numeric::ublas::matrix<FloatType> getTranslationMatrix();
protected:
    boost::numeric::ublas::vector<FloatType> m_location;
    boost::math::quaternion<FloatType> m_orientation;
};
我已经在.cpp文件中定义了成员函数:
//Quatcam.cpp
#include "Quatcam.h"
using namespace boost::numeric::ublas;
template<typename FloatType>
QuaternionCamera<FloatType>::QuaternionCamera()
    : m_location(3),
    m_orientation(1,0,0,0)
{
    m_location[0] = m_location[1] = m_location[2] = 0;
}
template<typename FloatType>
void QuaternionCamera<FloatType>::applyTranslation(boost::numeric::ublas::vector<FloatType> translationVector)
{
    m_location += translationVector;
}
template<typename FloatType>
boost::numeric::ublas::matrix<FloatType> QuaternionCamera<FloatType>::getTranslationMatrix()
{
    boost::numeric::ublas::matrix<FloatType> returnMatrix = boost::numeric::ublas::identity_matrix<FloatType>(4,4);
    boost::numeric::ublas::vector<FloatType> invTrans = -m_location;
    returnMatrix(3,0) = invTrans[0];
    returnMatrix(3,1) = invTrans[1];
    returnMatrix(3,2) = invTrans[2];
    return returnMatrix;
}

此代码本身将愉快地编译成.lib或.obj文件,但试图在原位使用类会导致链接器错误。下面是我的示例main.cpp试图使用类:

#include "Quatcam.h"
#include <boost/numeric/ublas/io.hpp>
#include <iostream>
int main(int argc, char** argv)
{
    QuaternionCamera<float> qcam;
    boost::numeric::ublas::vector<float> loc(3);
    loc[0] = 0;
    loc[1] = 5;
    loc[2] = 0;
    qcam.applyTranslation(loc);
    boost::numeric::ublas::matrix<float> qtm = qcam.getTranslationMatrix();
    std::cout << "qtm: "<< qtm << std::endl;
    return 0;
}

此代码链接失败,因为getTranslationMatrix和applyTranslation缺少符号。我认为这是因为我没有在技术上为float类型指定完整的函数专门化。


问题(s)

给定任何原子输入类型(float, double,甚至int等…)的行为是相同的,并且只影响答案的精度。

是否有一种方法可以强制编译器为它们发出特化而不必;

  1. 将所有函数定义移动到头文件中,或者;
  2. 显式地为所有数据类型创建专门化,这可能涉及大量的复制?

推荐链接

    为什么模板只能在头文件中实现?为什么c++模板定义需要在头文件中?

推荐作法

与其将定义从.cpp移到头文件中,不如将.cpp重命名为。tpp,并在Quatcam.h结尾添加#include "Quatcam.tpp"

这就是您通常拆分模板声明及其定义的方式,同时仍然有可用于实例化的定义。

注意:如果你遵循这条路,你不应该单独编译.tpp,就像你编译.cpp一样。


显式实例化

您可以在.cpp中显式实例化所讨论的模板,以便为链接器提供它们,但这要求您知道需要实例化的确切类型。

这意味着如果您只显式实例化QuaternionCamera<float>,如果main.cpp试图使用QuaternionCamera<double>,您仍然会得到链接器错误。

没有办法强制实例化所有"原子输入类型",您必须显式地将它们全部写出来。

 template class QuaternionCamera<float>;  // explicit instantiation
 template class QuaternionCamera<double>; // etc, etc...

应该把这些函数放在头文件中,而不是放在.cpp源代码中。

编译器只在模板实参推导完成后才创建函数实例化。生成的目标文件将包含针对模板所使用的每种类型的编译函数。

但是,.cpp文件是单独编译的。因此,当您编译Quatcam.cpp时,编译器不会为该类型找到任何实例化,也不会创建函数体。这就是为什么会出现链接器错误。

简单地说,你的标题应该是这样的:

template<typename T>
class Foo {
    void Print();
    T data;
};
// If template arguments are specified, function body goes to .cpp
template<>
void Foo<float>::Print();
// Template arguments are incomplete, function body should remain in the header
template<typename T>
void Foo<T>::Print() {
    std::cout << data;
}

这应该是。cpp的源代码:

template<>
void Foo<float>::Print() {
    std::cout << floor(data);
}