テンプレートパラメータとしてfloat
を使用しようとすると、コンパイラはこのコードを要求しますが、int
は正常に機能します。
float
をテンプレートパラメーターとして使用できないためですか?
#include<iostream>
using namespace std;
template <class T, T defaultValue>
class GenericClass
{
private:
T value;
public:
GenericClass()
{
value = defaultValue;
}
T returnVal()
{
return value;
}
};
int main()
{
GenericClass <int, 10> gcInteger;
GenericClass < float, 4.6f> gcFlaot;
cout << "\n sum of integer is "<<gcInteger.returnVal();
cout << "\n sum of float is "<<gcFlaot.returnVal();
return 0;
}
エラー:
main.cpp: In function `int main()':
main.cpp:25: error: `float' is not a valid type for a template constant parameter
main.cpp:25: error: invalid type in declaration before ';' token
main.cpp:28: error: request for member `returnVal' in `gcFlaot',
which is of non-class type `int'
Ron Pentonによる "ゲームプログラマ向けのデータ構造"を読んでいますが、著者はfloat
を渡しますが、試してみるとコンパイルしていないようです。
現在のC++標準では、float
(実数)または文字列リテラルをテンプレートの非型パラメーターとして使用できません。もちろん、float
とchar *
は通常の引数としてタイプします。
おそらく、著者は現在の標準に従っていないコンパイラを使用しているのでしょうか?
標準では、浮動小数点はnon-type template-argumentsとして許可されていません。これは、C++ 11標準の次のセクションで読むことができます;
14.3.2/1テンプレートの非型引数[temp.arg.nontype]
タイプ、テンプレート以外のテンプレートパラメータのテンプレート引数は、次のいずれかです。
整数型または列挙型の非型テンプレートパラメーターの場合、テンプレートパラメーターの型の変換された定数式(5.19)。
型のないテンプレートパラメータの名前。または
静的ストレージ期間と外部または内部リンケージを持つオブジェクトのアドレス、または外部または内部リンケージを持つ関数のアドレスを指定する定数式(5.19)。関数テンプレートと関数template-idを含むが、非静的クラスメンバを除き、表現(無視括弧)&&id-expression。ただし、名前が関数または配列を参照する場合は&を省略でき、対応するtemplate-parameterが参照である場合は省略されます。または
nULLポインター値(4.10)に評価される定数式。または
nullメンバーポインター値(4.11)に評価される定数式。または
5.3.1で説明されているように表現されたメンバーへのポインター。
これはおそらく、浮動小数点計算を正確に表現できないという事実によるものです。許可された場合、これを行うと、誤った/奇妙な動作が発生する可能性があります。
func<1/3.f> ();
func<2/6.f> ();
同じ関数を2回呼び出すつもりでしたが、2つの計算の浮動小数点表現がexactlyであることが保証されていないため、そうではないかもしれません。
C++11
かなり高度なconstant-expressions(constexpr)を書くことができます浮動小数点値のコンパイル時の分子/分母を計算し、これら2つを個別の整数引数として渡します。
互いに近い浮動小数点値が同じ分子/分母を生成するように、ある種のしきい値を定義することを忘れないでください。 非型テンプレート引数として浮動小数点値を許可しない理由として前述した同じ結果。
これが制限である理由の1つを提供するだけです(少なくとも現在の標準では)。
テンプレートの特殊化と一致する場合、コンパイラは非型引数を含むテンプレート引数と一致します。
本質的に、浮動小数点値は正確ではなく、その実装はC++標準で指定されていません。その結果、2つの浮動小数点の非型引数が実際にいつ一致するかを判断するのは困難です。
template <float f> void foo () ;
void bar () {
foo< (1.0/3.0) > ();
foo< (7.0/21.0) > ();
}
これらの式は必ずしも同じ「ビットパターン」を生成するわけではないため、同じ特殊化を使用することを保証することはできません。
実際、テンプレートパラメータとしてフロートリテラルを使用することはできません。 セクション14.1(「非タイプテンプレートパラメーターは、次の(オプションでcv修飾された)タイプのいずれかを持つ必要があります...」)標準の。
テンプレートパラメータとしてフロートへの参照を使用できます。
template <class T, T const &defaultValue>
class GenericClass
.
.
float const c_four_point_six = 4.6; // at global scope
.
.
GenericClass < float, c_four_point_six> gcFlaot;
パラメータをconstexprsとして独自のクラスでラップします。事実上、これは一連のフロートでクラスをパラメーター化するという特性に似ています。
class MyParameters{
public:
static constexpr float Kd =1.0f;
static constexpr float Ki =1.0f;
static constexpr float Kp =1.0f;
};
そして、クラス型をパラメーターとして取るテンプレートを作成します
template <typename NUM, typename TUNING_PARAMS >
class PidController {
// define short hand constants for the PID tuning parameters
static constexpr NUM Kp = TUNING_PARAMS::Kp;
static constexpr NUM Ki = TUNING_PARAMS::Ki;
static constexpr NUM Kd = TUNING_PARAMS::Kd;
.... code to actually do something ...
};
そして、次のように使用します...
int main (){
PidController<float, MyParameters> controller;
...
...
}
これにより、コンパイラは、同じパラメーターパックを使用して各テンプレートのインスタンス化に対してコードの単一インスタンスのみが作成されることを保証できます。これですべての問題が回避され、テンプレートクラス内でconstexprとしてfloatとdoubleを使用できます。
タイプごとに固定されたデフォルトを使用してもよい場合は、タイプを作成して定数として定義し、必要に応じて特殊化できます。
template <typename T> struct MyTypeDefault { static const T value; };
template <typename T> const T MyTypeDefault<T>::value = T();
template <> struct MyTypeDefault<double> { static const double value; };
const double MyTypeDefault<double>::value = 1.0;
template <typename T>
class MyType {
public:
MyType() { value = MyTypeDefault<T>::value; }
private:
T value;
};
C++ 11がある場合、デフォルト値を定義するときにconstexprを使用できます。 C++ 14では、MyTypeDefaultをテンプレート変数にすることができます。これは、構文的に少し簡潔です。
//C++14
template <typename T> constexpr T MyTypeDefault = T();
template <> constexpr double MyTypeDefault<double> = 1.0;
template <typename T>
class MyType {
private:
T value = MyTypeDefault<T>;
};
いつでも偽造できます...
#include <iostream>
template <int NUM, int DEN>
struct Float
{
static constexpr float value() { return (float)NUM / (float)DEN; }
static constexpr float VALUE = value();
};
template <class GRAD, class CONST>
struct LinearFunc
{
static float func(float x) { return GRAD::VALUE*x + CONST::VALUE; }
};
int main()
{
// Y = 0.333 x + 0.2
// x=2, y=0.866
std::cout << " func(2) = "
<< LinearFunc<Float<1,3>, Float<1,5> > ::func(2) << std::endl;
}
参照: http://code-slim-jim.blogspot.jp/2013/06/c11-no-floats-in-templates-wtf.html
Doubleをコンパイル時の定数にする必要がない場合は、ポインターとして渡すことができます。
#include <iostream>
extern const double kMyDouble = 0.1;;
template <const double* MyDouble>
void writeDouble() {
std::cout << *MyDouble << std::endl;
}
int main()
{
writeDouble<&kMyDouble>();
return 0;
}
他の答えは、おそらく浮動小数点テンプレートパラメータが必要ない理由を示していますが、本当の意味でのIMOは、「==」を使用する等式とビット単位の等式は同じではないということです。
-0.0 == 0.0
、ただし0.0
および-0.0
ビット単位で等しくない
NAN != NAN
どちらの種類の平等も、タイプの平等の良い候補ではありません。もちろん、ポイント2は==
型の等価性を判断するには無効です。代わりにビット単位の等式を使用できますが、x != y
は、MyClass<x>
およびMyClass<y>
は異なるタイプ(2ごと)であり、かなり奇妙です。
固定精度のみを表現する場合は、このような手法を使用して、floatパラメーターをintに変換できます。
たとえば、2桁の精度(100で除算)を想定して、成長因子1.75の配列を次のように作成できます。
template <typename _Kind_, int _Factor_=175>
class Array
{
public:
static const float Factor;
_Kind_ * Data;
int Size;
// ...
void Resize()
{
_Kind_ * data = new _Kind_[(Size*Factor)+1];
// ...
}
}
template<typename _Kind_, int _Factor_>
const float Array<_kind_,_Factor_>::Factor = _Factor_/100;
テンプレート引数リストで1.75の175としての表現が気に入らない場合は、常にいくつかのマクロでラップできます。
#define FloatToIntPrecision(f,p) (f*(10^p))
template <typename _Kind_, int _Factor_=FloatToIntPrecision(1.75,2)>
// ...