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] への暗黙的な変換などを使用して、float を長さに変換する特殊なバージョンがあります。

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

float を length に変換してみましょう:

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. バリアント追加機能テンプレート

2番目のアプローチを機能させたい場合は、次をお勧めします。

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 に答える