2

考虑以下模板来存储简化的 SI 单位:

template < int Mass, int Length, int Time >
class TUnit
{
public:
    // Used type.
typedef float DataType;
    .....
}

例如 SI 单位“长度”定义如下:

typedef TUnit< 0, 1, 0 > Length;

存在一个将 DataTypes 转换为 TUnits 的全局通用函数:

template < int Mass, int Length, int Time >
TUnit< Mass, Length, Time > convert( const typename TUnit< Mass, Length, Time >::DataType& src );

我们有一个专门的版本来将浮点数转换为长度,例如从 [km] 到 [m] 的隐式转换:

template < >
Tools::DataTypes::Length convert( const Tools::DataTypes::Length::DataType& src );

现在尝试将浮点数转换为长度:

float f = 1.0;
Length l = ::convert( f )

现在 VC2012 无法编译并出现错误代码:

error C2783: could not deduce template argument for 'Mass'
             could not deduce template argument for 'Length'
             could not deduce template argument for 'Time'

为了解决这个问题,我将代码更改为:

float f = 1.0;
Length l = ::convert< 0, 1, 0 >( f )

很好,但这不是我想要的:) 我首选的语法是:

float f = 1.0;
Length l = ::convert< Length >( f )

我想我必须将通用模板函数的签名更改为:

template < TUnit< int Mass, int Length, int Time > >
TUnit< Mass, Length, Time > convert( const typename TUnit< Mass, Length, Time >::DataType& src );

但是,这种语法当然是错误的。有什么提示可以解决这个问题吗?

4

2 回答 2

1

1. 变体(通过模板类)

您不能专门化功能模板。如果您真的想这样做,请创建一个具有单个静态函数的类并专门化该类。然后,您可以使函数调用该类的静态方法。

template < int Mass, int Length, int Time >
TUnit< Mass, Length, Time > convert( const typename TUnit< Mass, Length, Time >::DataType& src ) {
  return Converter<Mass, Length, Time>::call();
}

然后定义模板类:

template < int Mass, int Length, int Time >
struct Converter {
   static TUnit< Mass, Length, Time > call( const typename TUnit< Mass, Length, Time >::DataType& src) {
     [...]
   }
};

及其专业化,例如长度:

template < >
struct Converter<0,1,0> {
   static const int Mass = 0;
   static const int Length = 1;
   static const int Time = 0;

   static TUnit< Mass, Length, Time > call( const typename TUnit< Mass, Length, Time >::DataType& src) {
     [...]
   }
};

2.变体重载

或者,我建议改用函数重载,这基本上是等价的,我的意思是

// NOTE: no `template < >`
Tools::DataTypes::Length convert( const Tools::DataTypes::Length::DataType& src );

由于 C++ 重载机制,这个重载函数优先于函数模板convert<Mass, Length, Time>。只要您不使用显式模板参数调用该函数。

3.变体附加功能模板

如果您想采用第二种工作方式,那么我建议:

template < int Mass_, int Length_, int Time_ >
class TUnit
{
public:
  static const int Mass = Mass_;
  static const int Length = Length_;
  static const int Time = Time_;
[...]

接着

template < class TUnitT >
TUnitT convert( const typename TUnitT::DataType& src ) {
  return convert<TUnitT::Mass, TUnitT::Length, TUnitT::Time>(src);
}

但我会推荐第一种方法。

于 2012-11-12T18:46:52.410 回答
0

当然,您可以使用模板作为模板参数。在这种情况下,它将是一种特殊类型,因此您甚至不必使用模板模板习语。写这样的东西:

template < class TUNIT>
TUNIT convert( const typename TUNIT::DataType& src );

template < int M, int A, int B>
TUnit<M,A,B> convert( const typename TUnit<M,A,B>::DataType& src )
{
    typedef TUnit< M,A,B > TUnit_;
    return ::convert<TUnit_>(src);
}

...

typedef TUnit< 0, 1, 0 > Length;

...

float f = 1.0f;
Length l = ::convert< Length >( f );

这将编译得很好。

于 2012-11-12T18:51:55.587 回答