2

独自のSI単位系を実装しました。算術演算を使用すると、結果のSI単位が変わる可能性があります。例:(メートル/秒)/メートル=1/秒。

これで、単純な3DVectorクラスも作成しました。このベクトルは一般的であり、私のSI単位系でも使用できます。そこで、単純な除算演算子を実装しました。次のコードを参照してください。

// Determine result type of Lhs / Rhs:
template < class Lhs, class Rhs >
    struct TV3TypeV3Div { typedef BOOST_TYPEOF( Lhs( ) / Rhs( ) ) type; };

// Vector / Vector:
template < class Lhs, class Rhs >
RobotTools::DataTypes::TV3Type< typename TV3TypeV3Div< Lhs, Rhs >::type > operator/( const RobotTools::DataTypes::TV3Type< Lhs >& lhs,
                                                                                     const RobotTools::DataTypes::TV3Type< Rhs >& rhs )
{
    // do something useful
    return RobotTools::DataTypes::TV3Type< typename TV3TypeV3Div< Lhs, Rhs >::type >( 0, 0, 0 );
}

// Vector / Vector
RobotTools::DataTypes::TV3Type< Tools::DataTypes::Length > vl;
vl / vl;  // Ok this works

コンパイル時に、TV3TypeV3Div構造体を使用して正しいリターンタイプが決定されます。これは機能します。

ここで、演算子を拡張したいと思います。スカラー型のベクトルも計算したい。だから私はこの演算子を書きました:

// Vector / Scalar
template < class Lhs, class Rhs >
RobotTools::DataTypes::TV3Type< typename TV3TypeV3Div< Lhs, Rhs >::type > operator/( const RobotTools::DataTypes::TV3Type< Lhs >& lhs,
                                                                                     const Rhs& rhs )
{
    // do something useful
    return RobotTools::DataTypes::TV3Type< typename TV3TypeV3Div< Lhs, Tools::DataTypes::Length >::type >( 0, 0, 0 );
}

// Vector / Scalar
RobotTools::DataTypes::TV3Type< Tools::DataTypes::Length > vl;
Tools::DataTypes::Length sl;
vl / sl;  // Ok nice it works too

ここまでは順調ですね。問題は、2番目の演算子(ベクトル/スカラー)を定義するときに、この演算子が非常に一般的であるため、コンパイラーがベクトル/ベクトル除算にも使用したいということです。しかし、Lhs()/ Rhs()が次のようになっているため、失敗します。

Lhs = Tools :: DataTypes::LengthおよびRhs=RobotTools :: DataTypes :: TV3Type

定義されてない。これは正しく、与えられたエラーを理解しています。私が理解していないのは、コンパイラがVector/Vector演算子を使用しないということです。

  • どの演算子を使用するかについてコンパイラーにヒントを与える可能性はありますか?
  • 要件を満たすために演算子を書き直す可能性はありますか?
4

1 に答える 1

2

コンパイラは、ベクトル/ベクトル除算にベクトル/スカラー演算子を使用したくありません。一致するかどうかを確認したいだけです。

完全に一般的な除算演算子を宣言する (定義しない) 場合

template <typename Lhs, typename Rhs>
struct InvalidDivision {};
template <typename Lhs, typename Rhs>
InvalidDivision<Lhs, Rhs> 
operator/(const Lhs& lhs, const Rhs& rhs); // do not define

次に、コードをコンパイルしてリンクする必要があります。Vector/Scalar オーバーロードは考慮され、Vector/Vector の方が一致するため拒否されます。

その欠点は、除算が定義されていないものを除算すると、コンパイラーはInvalidDivision<something,other>通常のエラーを出す代わりに文句を言うことです。

おそらく、これは SFINAE やその他の高度な魔法を使用することで改善される可能性があります。

更新:より詳細な説明

元のバージョンのコードでは何が起こっているのでしょうか?

ユーザー定義型の除算演算子を呼び出そうとしています。名前が付けられた 2 つの関数がoperator/あり、コンパイラは両方を考慮して、どちらがより適切に一致するかを見つけようとします。

最初に Vector/Vector を検討してoperator/ください。Lhsコンパイラは、とRhsテンプレート パラメーターの両方がLength(簡潔にするために名前空間を省略しています) であると推測します。次に、それらを のパラメータに代入しTV3TypeV3Div、 の内部を計算し、もであるとTV3TypeV3Div判断し、最後に の戻り値の型を計算します。TV3TypeV3Div<Lhs,Rhs>::typeLengthoperator/TV3Type<Length>

次に、 Vector/Scalar を検討してくださいoperator/。コンパイラはそれが であると推測しLhsますがLengthRhsですTV3Type<Length>。次に、これらの型を のパラメータに代入TV3TypeV3Divし、 の内部を計算しようとしますTV3TypeV3Div。物事が壊れるところはここにあります:operator/受け入れるLengthTV3Type<Length>. したがって、 を計算することはできませんTV3TypeV3Div<Lhs,Rhs>::type。コンパイラはエラーを出力します。

次に、ジェネリックoperator/が宣言されたときに何が起こるかを考えてみましょう。これで、and !を受け入れる があります。(または、その点については、他の引数のペア)。そのため、コンパイラは喜んで Vector/Scalar の戻り値の型を計算します。そのため、Vector/Vector と Vector/Scalar の両方のオーバーロードを考慮することが可能になりました。operator/LengthTV3Type<Length>TV3TypeV3Div<Lhs,Rhs>::typeoperator/operator/

operator/コンパイラは、オーバーロード解決のジェネリックも認識して考慮するようになりました。3 つのオーバーロードはすべて一致します。しかし、Vector/Vector のオーバーロードは、Vector/Scalar または generic よりも特殊化されてoperator/いるため、より適しています。

更新 2

これを行うことができるはずです:

namespace Detail
{
    template <typename Lhs, typename Rhs>
        struct DoNotUse {};
    template <typename Lhs, typename Rhs>
        DoNotUse<Lhs, Rhs> operator/(const Lhs& lhs, const Rhs& rhs);
    template < class Lhs, class Rhs >
        struct TV3TypeV3Div { typedef BOOST_TYPEOF( Lhs( ) / Rhs( ) ) type; };
}
using Detail::TV3TypeV3Div;

これにより、一般的な演算子 / がTV3TypeV3Div.

于 2012-07-01T12:55:47.027 に答える