3

乗算演算の結果の型が入力型の関数になる固定小数点型の複素数クラスを実装しようとしています。複素数を複素数で掛けたり、複素数を実数で掛けたりできる関数が必要です。

これは本質的にコードの簡略化されたバージョンです。A は私の複合型です。

template<typename T1, typename T2> struct rt {};

template<> struct rt<double, double> { 
    typedef double type;
};
//forward declaration
template<typename T> struct A;

template<typename T1, typename T2>
struct a_rt {
    typedef A<typename rt<T1,T2>::type> type;
};

template <typename T>
struct A {
    template<typename T2>
    typename a_rt<T,T2>::type operator*(const T2& val) const {
        typename a_rt<T,T2>::type ret;
        cout << "T2& called" << endl;
        return ret;
    }
    template<typename T2>
    typename a_rt<T,T2>::type operator*(const A<T2>& val) const {
        typename a_rt<T,T2>::type ret;
        cout << "A<T2>& called" << endl;
        return ret;
    }
};

TEST(TmplClassFnOverload, Test) {
    A<double> a;
    A<double> b;
    double c;
    a * b;
    a * c;
}

a_rtコンパイラが と を使用してテンプレートをインスタンス化しようdoubleとしているため、コードはコンパイルに失敗しますA<double>。コンパイラーはより特殊化されたものを選択する必要があるため、引数としてのみインスタンス化されるため、内部で何が起こっているのかわかりoperator*(A<double>&)ませんa_rt<double, double>

これがうまくいかない理由を教えてください。これが制限である場合、これを回避するにはどうすればよいですか。

ありがとうございます!

unittest.cpp: In instantiation of 'a_rt<double, A<double> >':
unittest.cpp:198:   instantiated from here 
unittest.cpp:174: error: no type named 'type' in 'struct rt<double, A<double> >' 

アップデート

コンパイラは次の変更に満足しているようです。ここで見逃している微妙な点があります。どちらの場合もコンパイラが何をしているのかを説明してくれる人に感謝します。

    template<typename T2>
    A<typename rt<T,T2>::type> operator*(const T2& val) const {
        A<typename rt<T,T2>::type> ret;
        cout << "T2& called" << endl;
        return ret;
    }
    template<typename T2>
    A<typename rt<T,T2>::type> operator*(const A<T2>& val) const {
        A<typename rt<T,T2>::type> ret;
        cout << "A<T2>& called" << endl;
        return ret;
    }
4

2 に答える 2

4

C++ での関数呼び出しの解決は、次の 5 つのフェーズで進行します。

  1. name lookup : これは の 2 つのバージョンを見つけます operator*
  2. テンプレート引数推定: これは、ステップ 1 で見つかったすべての関数に適用されます)
  3. オーバーロードの解決: 最適な一致が選択されます
  4. アクセス制御: 実際にベストマッチを呼び出すことができますか (つまり、それはプライベートメンバーではありません)
  5. virtuality : 仮想関数が関係している場合、vtable でのルックアップが必要になる場合があります

最初に、戻り値の型が推測されることは決してないことに注意してください。return type でオーバーロードすることはできません。へのテンプレート引数operator*が推定され、戻り値の型テンプレートに代入されます。

では、電話で何が起こるのa * b;でしょうか? まず、両方のバージョンのoperator*引数が推定されます。最初のオーバーロードの場合、 は でT2あると推定されA<double>、2 番目のオーバーロードの場合T2は に解決されdoubleます。複数のオーバーロードがある場合、標準は次のように述べています。

14.7.1 暗黙のインスタンス化 [temp.inst] 節 9

関数テンプレートまたはメンバー関数テンプレートの特殊化がオーバーロードの解決を伴う方法で使用される場合、特殊化の宣言は暗黙的にインスタンス化されます (14.8.3)。

そのため、候補関数のセットが生成されているときの引数演繹の最後に (つまり、オーバーロード解決の前に) テンプレートがインスタンス化rtされ、ネストされた がないためエラーが発生しますtype。これが、より特化した 2 番目のテンプレートが選択されない理由です。オーバーロードの解決は行われません。この置換の失敗はエラーではないと予想していたかもしれません。ただし、標準は次のように述べています。

14.8.2 テンプレート実引数控除 [temp.deduct] 節 8

置換の結果が無効な型または式になる場合、型推定は失敗します。無効な型または式は、置換された引数を使用して記述した場合に不適切な形式になるものです。関数の型とそのテンプレート パラメーターの型の直接のコンテキストで無効な型と式のみが推定エラーになる可能性があります。[ 注: 置換された型と式の評価は、クラス テンプレートの特殊化および/または関数テンプレートの特殊化のインスタンス化、暗黙的に定義された関数の生成などの副作用をもたらす可能性があります。 context」であり、プログラムの形式が正しくない可能性があります。— エンドノート]

元のコードでは、typename a_rt<T,T2>::type戻り値の型は即時のコンテキストではありません。テンプレートのインスタンス化中にのみ評価され、ネストされていないことがtypeエラーrtになります。

更新されたコードA<typename rt<T,T2>::type>では、戻り値の型は即時のコンテキストであり、Substitution Failure is Not An Error (SFINAE) が適用されます。推定されていない関数テンプレートは、オーバーロード解決セットから単純に削除され、残りのテンプレートが呼び出されています。

更新されたコードでは、出力は次のようになります。

> A<T2>& called     
> T2& called
于 2012-08-08T06:53:41.990 に答える
0

あなたの前方宣言はclassを使用します:

template<typename T> class A;

しかし、あなたの定義はstructを使用しています:

template <typename T>
struct A {

それ以外は特に問題は見られません...

于 2012-08-08T05:10:56.710 に答える