7

私が尋ねた最後の質問は、別のことを理解しようとしたときにつまずいたものでした... 私も理解できません(私の日ではありません)。

これは非常に長い質問文ですが、少なくとも、この質問が私だけでなく多くの人に役立つことを願っています.

私が持っているコードは次のとおりです。

template <typename T> class V;
template <typename T> class S;

template <typename T>
class V
{
public:
 T x;

 explicit V(const T & _x)
 :x(_x){}

 V(const S<T> & s)
 :x(s.x){}
};

template <typename T>
class S
{
public:
 T &x;

 explicit S(V<T> & v)
 :x(v.x)
 {}
};

template <typename T>
V<T> operator+(const V<T> & a, const V<T> & b)
{
 return V<T>(a.x + b.x);
}

int main()
{
 V<float> a(1);
 V<float> b(2);
 S<float> c( b );

 b = a + V<float>(c); // 1 -- compiles
 b = a + c;           // 2 -- fails
 b = c;               // 3 -- compiles

 return 0;
}

式 1 と 3 は完全に機能しますが、式 2 はコンパイルされません。

私が正しく理解していれば、何が起こるか:

式1

  1. cconst、標準の変換シーケンス (修飾変換が 1 つだけで構成される) を使用して暗黙的に変換されます。
  2. V<float>(const S<T> & s)が呼び出され、オブジェクトが生成したテンポラルが生成されます (これをtconst V<float>と呼びましょう)。これは一時的な値であるため、すでに const 修飾されています。
  3. aはcと同様に const に変換されます。
  4. operator+(const V<float> & a, const V<float> & b)が呼び出され、 qconst V<float>と呼べるタイプのテンポラルが生成されます。
  5. デフォルトV<float>::operator=(const & V<float>)は呼び出されます。

私はここまで大丈夫ですか?私が最も微妙な間違いを犯した場合でも、私に知らせてください.暗黙のキャストについてできるだけ深く理解しようとしています...

式 3

  1. cは に変換されV<float>ます。そのために、ユーザー定義の変換シーケンスがあります:
    1.1. 最初の標準変換:修飾変換S<float>を介して。 1.2. ユーザー定義の変換:コンストラクター経由。 1.3秒基準換算:経由資格換算へ。const S<float>
    const S<float>V<float>V<float>(const S<T> & s)
    V<float>const V<float>
  2. デフォルトV<float>::operator=(const & V<float>)は呼び出されます。

式2?

私が理解していないのは、なぜ 2 番目の式に問題があるのか​​ということです。次のシーケンスができないのはなぜですか?

  1. cは に変換されV<float>ます。そのために、ユーザー定義の変換シーケンスがあります:
    1.1. 初期標準変換:修飾変換S<float>を介して。 1.2. ユーザー定義の変換:コンストラクター経由。 1.3。最終標準変換:資格変換 を介して。const S<float>
    const S<float>V<float>V<float>(const S<T> & s)
    V<float>const V<float>
  2. 手順 2 ~ 6 は式 1 の場合と同じです。

C++ 標準を読んだ後、私は次のように言いました。おそらく問題は 13.3.3.1.2.3 に関係しています! どの州:

ユーザー定義の変換がテンプレート変換関数によって指定されている場合、2 番目の標準変換シーケンスは完全一致ランクでなければなりません。

しかし、資格変換には完全一致ランクがあるため、それは当てはまりません...

本当に手がかりがない...

さて、あなたが答えを持っているかどうかにかかわらず、ここまで読んでくれてありがとう:)

4

3 に答える 3

12

Edric が指摘したように、変換はテンプレートの引数推定中に考慮されません。ここでは、引数の型からテンプレート パラメーター T を推測できる 2 つのコンテキストがあります。

template<class T>
v<T> operator+(V<T> const&, V<T> const&);
               ~~~~~~~~~~~  ~~~~~~~~~~~~

V<float>しかし、左側に を、右側に S を指定して、この関数テンプレートを呼び出そうとします。テンプレートの引数推定は、左辺で T=float になり、右辺でエラーが発生します。これは、 T がないため、V<T>等しい となるからですS<T>。これは、テンプレート引数推定の失敗と見なされ、テンプレートは単純に無視されます。

変換を許可したい場合は、 operator+ をテンプレートにしないでください。次のトリックがあります: V のクラス テンプレート内でインライン フレンドとして定義できます。

template<class T>
class V
{
public:
   V();
   V(S<T> const&); // <-- note: no explicit keyword here

   friend V<T> operator+(V<T> const& lhs, V<T> const& rhs) {
      ...
   }
};

このように、オペレーターはもはやテンプレートではありません。したがって、テンプレート引数の推定は必要なく、呼び出しが機能するはずです。演算子は、左側がV<float>. 右辺も適切に a に変換さV<float>れます。

特定の引数のテンプレート引数推定を無効にすることもできます。例えば:

template<class T>
struct id {typedef T type;};

template<class T>
T clip(
   typename id<T>::type min,
   T value,
   typename id<T>::type max )
{
   if (value<min) value=min;
   if (value>max) value=max;
   return value;
}

int main() {
   double x = 3.14;
   double y = clip(1,x,3); // works, T=double
}

最初と最後の引数の型が int であっても、 はid<T>::typeいわゆる * deducible context` ではないため、テンプレートの引数推定では考慮されません。したがって、T は 2 番目の引数に従ってのみ推定され、結果として T=double となり、矛盾はありません。

于 2010-10-08T06:37:09.017 に答える
4

テンプレートの一致を考慮する場合、暗黙的な変換は使用されません。したがって、次の簡単な例では:

template < typename T >
void foo( T t1, T t2 ) { /* do stuff */ }

int main( int argc, char ** argv ) {
    foo( 1, 1.0 );
    return 0;
}

いずれかの引数が暗黙的に他の型 (int <-> double) に変換されたとしても、それはコンパイルされません。

于 2010-10-08T06:19:47.250 に答える
0

推測ですが、式 2 に + c を追加する方法を見つけようとしているときに、おそらくコンパイラは V->S からの変換と S->V からの変換を区別できません。残りの使用可能な関数のためにコンパイルを続行できるものを選択しますが、コンパイラはおそらく「先読み」(いわば) ではなく、検索を試みる前にアップコンバージョンのあいまいさと混乱しています。 「+」演算子。

もちろん、コンパイル エラーを追加すると、問題の明確化にも役立つ可能性があります...

于 2010-10-08T06:18:45.317 に答える