C ++テンプレート関数はひどいデフォルト値を取得します
-
18-09-2020 - |
質問
私はC ++で本物の脳のスコーサにぶつかった、それは私には起こりません。
問題の要旨は、My(テンプレート)関数の呼び出し時に、私がデフォルトを定義した引数がスクランブルされていることがわかります。デフォルトで関数を呼び出すと、
マイテンプレート関数は次のように宣言されています:
template <typename T>
vector2<T> transform(vector2<T> const &vec, matrix4<T> const &m, T z = T(0), T w = T(1));
.
後で、同じヘッダーで、次のように定義されています。
template <typename T>
inline vector2<T> transform(vector2<T> const &vec, matrix4<T> const &m, T z, T w)
{
vector4<T> res = m * vector4<T>(vec.x, vec.y, z, w);
return vector2<T>(res.x, res.y);
}
.
これをデフォルトで呼び出すと(transform(vector2<double>(0, 1), view_transform)
)私は予想される値を取得しません。 VC ++のデバッガを使用したtransform
にステップインすると、z
とw
が「面白い」値(私の経験では何かが正しく初期化されていないことを意味します)。
例面白い値は、0.0078125000000000000と2.104431116947E-317#den
今私はC ++ FAQ Liteで答えを見つけようとしました。 Schubertで自分自身を落ち着かせようとしましたが、私の人生がそれを把握できません。私はそれが本当にシンプルであると思います、そして私は仕事でいくつかの種類のテンプレートのTomfolelyです。
私が期待して欲しいデフォルト値を得る方法はあります、そしてそれは私にこれをするのですか?
編集1:
変更を変更したら、代わりにfloatsを使用します(transform(vector2<float>(0, 1), view_transform)
)問題は解消されます。 T
= double
の場合にのみ発生します。
編集2:
double
とfloat
に2つの特殊化がある場合にのみ発生します。 1つの場所でフロートの専門化を使用すると、二重専門化は奇妙なデフォルト値を取得します。私がすべての場所を変更した場合、その関数が呼び出されるので、それは2倍の問題を使用します「消えます」。それでも私はまだ理解していませんが、それは故障したオフセットやz
とw
を設定するときのものであるようなものです。
編集3:
C ++暗号からの物語:
#include <sgt/matrix4.hpp>
int main(int argc, char *argv[])
{
sgt::matrix4<double> m0(
2, 0, 0, 1,
0, 2, 0, 1,
0, 0, 1, 0,
0, 0, 0, 1);
m0 *= m0;
sgt::vector2<double> blah0 = sgt::transform(sgt::vector2<double>(1, 0), m0);
sgt::matrix4<float> m1(
2, 0, 0, 1,
0, 2, 0, 1,
0, 0, 1, 0,
0, 0, 0, 1);
m1 *= m1;
sgt::vector2<float> blah1 = sgt::transform(sgt::vector2<float>(1, 0), m1);
printf("%f", blah0.x);
printf("%f", blah1.x);
}
.
Matrix4.hpp:
// ...
template <typename T>
vector2<T> transform(vector2<T> const &vec, matrix4<T> const &m, T z = T(0), T w = T(1));
template <typename T>
inline vector2<T> transform(vector2<T> const &vec, matrix4<T> const &m, T z, T w)
{
vector4<T> res = m * vector4<T>(vec.x, vec.y, z, w);
return vector2<T>(res.x, res.y);
}
// ...
.
それを実行すると、デフォルトの引数が正しいですが、FLOATバージョンはどちらもゼロ(0.000000)の両方を取得します。
編集4:
解決
私のDev Studioでは次のことが失敗しました:
#include "stdafx.h"
#include <vector>
#include <iostream>
template <typename T>
std::vector<std::vector<T> > transform(std::vector<std::vector<T> > const &vec,
std::vector<std::vector<std::vector<std::vector<T> > > > const &m,
T z = T(0), T w = T(1));
template <typename T>
std::vector<std::vector<T> > transform(std::vector<std::vector<T> > const &vec,
std::vector<std::vector<std::vector<std::vector<T> > > > const &m,
T z, T w)
{
std::cout << "Z" << z << "\n";
std::cout << "W" << w << "\n";
return vec;
}
int _tmain(int argc, _TCHAR* argv[])
{
std::vector<std::vector<int> > xi;
std::vector<std::vector<std::vector<std::vector<int> > > > mi;
transform(xi,mi);
std::vector<std::vector<float> > xf;
std::vector<std::vector<std::vector<std::vector<float> > > > mf;
transform(xf,mf);
std::vector<std::vector<double> > xd;
std::vector<std::vector<std::vector<std::vector<double> > > > md;
transform(xd,md);
}
.
出力:
Z0
W1
Z0
W1.4013e-045
Z2.122e-314
W3.60689e-305
.
だから私はそれが期待どおりに機能しないと思います!!!
宣言事前宣言を取り除き、デフォルト引数をテンプレート関数に置くと、予想どおりに機能します。
#include "stdafx.h"
#include <vector>
#include <iostream>
template <typename T>
std::vector<std::vector<T> > transform(std::vector<std::vector<T> > const &vec,
std::vector<std::vector<std::vector<std::vector<T> > > > const &m
T z = T(0), T w = T(1))
{
std::cout << "Z" << z << "\n";
std::cout << "W" << w << "\n";
return vec;
}
int _tmain(int argc, _TCHAR* argv[])
{
std::vector<std::vector<int> > xi;
std::vector<std::vector<std::vector<std::vector<int> > > > mi;
transform(xi,mi);
std::vector<std::vector<float> > xf;
std::vector<std::vector<std::vector<std::vector<float> > > > mf;
transform(xf,mf);
std::vector<std::vector<double> > xd;
std::vector<std::vector<std::vector<std::vector<double> > > > md;
transform(xd,md);
}
.
これは期待通りに機能します。
これは、実際には関数前宣言ではなく、実際にはデフォルトのパラメータを持たず、パラメータリストにランダムな値を取得していないため、テンプレート事前宣言があります。
OK。これは予想通りに機能するはずです:
N2521を使用する
セクション14.7.1暗黙のインスタンス化
第9号
実装は、関数テンプレート、メンバーテンプレート、非仮想メンバ機能、メンバクラス、またはインスタンス化を必要としないクラステンプレートの静的データメンバを暗黙的にインスタンス化してはならない。実装が、仮想メンバ機能がインスタンス化されない場合に、実装がクラステンプレートの仮想メンバ関数を暗黙的にインスタンス化するかどうかは指定されていません。デフォルトの引数におけるテンプレートの専門化の使用は、デフォルトの引数の正当性を判断するためにクラステンプレートがインスタンス化されることを除いて、テンプレートを暗黙的にインスタンス化させないでください。 ②関数呼び出しでデフォルト引数を使用すると、デフォルトの引数の専門化が暗黙的にインスタンス化されます。
段落の太場は、デフォルトの引数のために作成された各特殊化が使用されたときに翻訳単位に暗黙的にインスタンス化されることを示すために(私にとって)、(私にとって)、(私にとって)そうであるようです。
段落11:
デフォルトの引数式を使用する方法で関数テンプレートFが呼び出された場合、依存名は検索され、セマンティクスの制約がチェックされ、デフォルトの引数式で使用されるテンプレートのインスタンス化が行われます。既定の引数式が、同じスコープを持つ関数テンプレートの専門化で使用されている式であったかのように、同じテンプレートパラメータとその時点で使用されている関数テンプレートFと同じアクセス。この分析はデフォルトの引数のインスタンス化と呼ばれます。その後、インスタンス化されたデフォルト引数はfの引数として使用されます。
デフォルトの引数がテンプレートパラメータであっても正しくインスタンス化されることを示します。
ええ、私は正しくそれを解釈したことを願っています。 : - )
他のヒント
コードは最適化されていますか?たぶんそれがデバッガがあなたに間違った値を示している理由です。
このより単純なコードを試してみました(G ++ 4.3.3)、それは期待どおりに機能します。
template <typename T>
T increment(T a, T b = T(1))
{
return a + b;
}
int main()
{
double a = 5.0;
std::cout << increment(a) << ", ";
std::cout << increment(a, 3.0) << "\n";
}
. これが機能するかどうかわかりませんが、あなたのデフォルト値のCスタイルキャストの代わりにstatic_castを使ってみてください。
*編集:どうやら、問題はコンパイラです。