2

の最後の質問std::common_type<X,Y>::typeへの答えとして、可能な場合は、元の の代わりに自動戻り型の宣言で使用することが提案されましたdecltype()。ただし、そうすると問題が発生しました(gcc 4.7.0を使用)。次の簡単なコードを検討してください

template<typename> class A;
template<typename X> class A {
  X a[3];
  template <typename> friend class A;
public:
  A(X a0, X a1, X a2) { a[0]=a0; a[1]=a1; a[2]=a2; }
  X operator[](int i) const { return a[i]; }
  X operator*(A const&y) const          // multiplication 0: dot product with self
  { return a[0]*y[0] + a[1]*y[1] + a[2]*y[2]; }
  template<typename Y>
  auto operator*(A<Y> const&y) const -> // multiplication 1: dot product with A<Y>
#ifdef USE_DECLTYPE
    decltype((*this)[0]*y[0])
#else
    typename std::common_type<X,Y>::type
#endif
  { return a[0]*y[0] + a[1]*y[1] + a[2]*y[2]; }
  template<typename Y>
  auto operator*(Y s) const ->          // multiplication 2: with scalar
#ifdef USE_DECLTYPE
    A<decltype((*this)[0]*s)>
#else
    A<typename std::common_type<X,Y>::type>
#endif
  { return A<decltype((*this)[0]*s)>(s*a[0],s*a[1],s*a[2]); }
};

int main()
{
  A<double> x(1.2,2.0,-0.4), y(0.2,4.4,5.0);
  A<double> z = x*4;
  auto dot = x*y;           // <-- 
  std::cout<<" x*4="<<z[0]<<' '<<z[1]<<' '<<z[2]<<'\n'
           <<" x*y="<<dot<<'\n';
}

USE_DECLTYPEがの場合#defined、コードは gcc 4.7.0 で正常にコンパイルおよび実行されます。しかし、それ以外の場合、 に示されている行はmain()乗算 2 を呼び出します。これは、間違っていないにしても奇妙に思えます。これは、使用の結果/副作用である可能性がありますstd::common_typeか、それとも gcc のバグですか?

戻り値の型は、多数の適切なテンプレート関数のどれが選択されるかには関係がないと常に考えていました...

4

1 に答える 1

8

使用する提案common_typeは偽物です。

他の質問で使用したdecltype問題は、単にGCCのバグでした。

使用時にこの質問で発生する問題は、式から取得するタイプを示しているcommon_typeためです。std::common_type<X, Y>::type

condition ? std::declval<X>() : std::declval<Y>()

つまり、anXとaのY両方をどのタイプに変換できるか。

一般に、これは、の結果とはまったく関係がありません。完全に異なる型を返すオーバーロードがあるx * y場合。XYoperator*

特定のケースでは、x*y両方の変数がタイプである式がありますA<double>。過負荷解決は、過負荷のそれぞれをチェックoperator*して、それが有効かどうかを確認しようとします。過負荷解決の一部として、このメンバー関数テンプレートをインスタンス化します。

template<typename Y>
    auto operator*(Y s) const ->
    A<typename std::common_type<X,Y>::type>;

A<double>テンプレートパラメータの代わりに使用しますYcommon_type<double, A<double>>式が有効でないため、インスタンス化しようとします。

condition ? std::declval<double>() : std::declval< A<double> >()

A<double>は無効です。これは、他の一般的なタイプに変換しdoubleたり、その逆を行ったりすることができないためです。

そのオーバーロードoperator*呼び出されるため、エラーは発生しません。どの演算子を呼び出すかを決定するためにテンプレートをインスタンス化する必要があるために発生し、インスタンス化する行為によってエラーが発生します。コンパイラーは、どの演算子を呼び出すかを決定することは決してありません。エラーは、それが遠くなる前にそれを停止します。

したがって、私が言ったように、使用する提案common_typeは偽物であり、SFINAEが引数タイプと一致しないメンバー関数テンプレートを無効にするのを防ぎます(正式には、置換エラーが「即時コンテキスト」の外部で発生するため、SFINAEはここでは機能しませんテンプレートの、つまりcommon_type、SFINAEが適用される関数シグネチャではないの定義内で発生します。)


特殊化することが許可されてstd::common_typeいるため、暗黙の変換なしで型を認識します。したがって、次のように、有効で型を生成するように特殊化できます。common_type<double, A<double>>::typedouble

namespace std {
  template<typename T>
    struct common_type<T, A<T>> { typedef T type; };
}

そうすることは非常に悪い考えです! 「これらの両方のタイプを安全に変換できるのはどのcommon_typeタイプですか?」に対する答えです。上記の特殊化は、「これらの型を乗算した結果は何ですか?」という答えを与えるためにそれを覆します。これはまったく別の質問です!真実であると専門化するのと同じくらい愚かでしょうis_integral<std::string>

「 exprなどの一般式のタイプは何ですか?」に対する答えが必要な場合 次に使用しますdecltype(expr)、それが目的です!

于 2012-06-15T18:37:15.783 に答える