10

私は C++ で本当の脳を焼き尽くすものに出くわしました。

問題の要点は、私の(テンプレート)関数の呼び出し時に、デフォルトを定義した引数の値がスクランブルされることです。デフォルトで関数を呼び出した場合にのみ発生します。

私のテンプレート関数は次のように宣言されています:

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);
}

これを defaults ( transform(vector2<double>(0, 1), view_transform)) で呼び出すと、期待する値が得られません。transformVC++ デバッガーに足を踏み入れると、「おかしな」値が表示さzwます (私の経験では、何かが正しく初期化されていないことを意味します)。

面白い値の例: 0.0078125000000000000 および 2.104431116947e-317#DEN

今、私は C++ FAQ Lite で答えを見つけようとしました。シューベルトで自分を落ち着かせようとさえしましたが、私は一生それを理解することができません. 私はそれが本当に単純で、ある種のテンプレートのおふざけが働いているのではないかと推測しています。

私が期待し、望んでいるデフォルト値を取得する方法はありますか?なぜそれが私にこれを行うのですか?

編集1:

代わりにフロートを使用するように変更呼び出しを行うと ( transform(vector2<float>(0, 1), view_transform))、問題は解決します。これはT=の場合にのみ発生するようdoubleです。

編集2:

と の 2 つの専門分野がある場合にのみ発生doublefloatます。ある場所で float 特殊化を使用すると、double 特殊化が奇妙なデフォルト値になります。関数が呼び出されるすべての場所を変更すると、問題が2倍になり、「なくなる」。zw. _

編集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);
}

// ...

それを実行すると、double-specialization のデフォルト引数は正しくなりますが、float バージョンは両方のデフォルト引数をゼロ (0.000000) として取得しz = 0ますw = 1

編集4:

接続の問題を作成しました。

4

3 に答える 3

5

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);
}

これは期待どおりに機能します。
これは、テンプレートの事前宣言が実際には関数の事前宣言ではないことに関係しているため、実際にはデフォルトのパラメーターがなく、パラメーターリストでランダムな値を取得しています。

わかった。標準の私の読書からではなく、これは期待どおりに機能するはずです:

n2521 の使用
セクション 14.7.1 暗黙的なインスタンス化
パラグラフ 9

実装は、関数テンプレート、メンバー テンプレート、非仮想メンバー関数、メンバー クラス、またはインスタンス化を必要としないクラス テンプレートの静的データ メンバーを暗黙的にインスタンス化してはなりません。仮想メンバー関数がインスタンス化されない場合、実装がクラス テンプレートの仮想メンバー関数を暗黙的にインスタンス化するかどうかは指定されていません。デフォルト引数でテンプレートの特殊化を使用しても、テンプレートが暗黙的にインスタンス化されることはありませんが、デフォルト引数の正確性を判断するために完全な型が必要な場合にクラス テンプレートがインスタンス化される場合があります。関数呼び出しでデフォルト引数を使用すると、デフォルト引数の特殊化が暗黙的にインスタンス化されます。

段落の太字部分は、(私には) デフォルト引数のために作成された各特殊化が、使用時に翻訳単位に暗黙的にインスタンス化されることを示しているようです。

パラグラフ 11:

デフォルトの引数式を使用する必要がある方法で関数テンプレート f が呼び出された場合、従属名が検索され、セマンティクスの制約がチェックされ、デフォルトの引数式で使用されるテンプレートのインスタンス化が行われます。デフォルトの引数式は、その時点で使用されている関数テンプレート f と同じスコープ、同じテンプレート パラメーター、および同じアクセス権を持つ関数テンプレートの特殊化で使用される式でした。この分析は、デフォルト引数のインスタンス化と呼ばれます。インスタンス化されたデフォルト引数は、f の引数として使用されます。

デフォルトの引数がテンプレート パラメーターであっても、正しくインスタンス化されることを示します。

私がそれを正しく解釈したことを願っています。:-)

于 2010-07-21T00:23:21.957 に答える
2

コードは最適化されていますか? おそらくそれが、デバッガーが間違った値を表示している理由です。

この単純なコード (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";
}
于 2010-07-21T00:06:12.537 に答える
1

これが機能するかどうかはわかりませんが、デフォルト値に C スタイルのキャストの代わりに static_cast を使用してみてください。

*編集:どうやら、問題はコンパイラです。

于 2010-07-21T00:01:12.857 に答える