7

いくつかの算術演算、たとえば固定小数点計算をカプセル化するクラスがあります。算術演算子をオーバーロードするというアイデアが好きなので、次のように記述します。

class CFixed
{
   CFixed( int   );
   CFixed( float );
};

CFixed operator* ( const CFixed& a, const CFixed& b )
{ ... }

それはすべて機能します。3 * CFixed(0)とCFixed(3)*10.0fと書くことができます。しかし、今では、整数オペランドを使用した演算子*をはるかに効果的に実装できることに気付きました。だから私はそれをオーバーロードします:

CFixed operator* ( const CFixed& a, int b )
{ ... }
CFixed operator* ( int a, const CFixed& b )
{ ... }

それでも機能しますが、CFixed(0)* 10.0fはオーバーロードされたバージョンを呼び出し、floatをintに変換します(floatをCFixedに変換することを期待していました)。もちろん、floatバージョンをオーバーロードすることもできますが、私にとってはコードの組み合わせ爆発のようです。回避策はありますか(またはクラスの設計が間違っていますか)?オーバーロードされたバージョンのoperator*をintのみで呼び出すようにコンパイラに指示するにはどうすればよいですか?

4

5 に答える 5

3

1つの引数だけで呼び出すことができるコンストラクターがある場合は、暗黙の変換演算子を効果的に作成しました。あなたの例では、aCFixedが必要な場合はいつでも、anintとaの両方floatを渡すことができます。これはもちろん危険です。コンパイラは、関数の宣言を含めるのを忘れたときに、吠えるのではなく、間違った関数を呼び出すコードをサイレントに生成する可能性があるためです。

したがって、経験則として、1つの引数だけで呼び出すことができるコンストラクターを作成する場合は常に(foo(int i, bool b = false)2つの引数を取る場合でも、これも1つの引数で呼び出すことができることに注意してください)、そのコンストラクターを作成する必要があります。explicit、暗黙の変換を実際に開始する必要がない限り、explicitコンストラクターは暗黙の変換のためにコンパイラーによって使用されません。

クラスを次のように変更する必要があります。

class CFixed
{
   explicit CFixed( int   );
   explicit CFixed( float );
};

この規則にはほとんど例外がないことがわかりました。(std::string::string(const char*)かなり有名なものです。)

編集:申し訳ありませんが、からintへの暗黙の変換を許可しないという点を見逃しましたfloat

これを防ぐために私が見る唯一の方法は、演算子も提供することfloatです。

于 2009-10-22T14:42:28.417 に答える
2

特殊化されたバージョンを任意の整数型 (特にintだけでなく) に選択したいと仮定すると、それをテンプレート関数として提供し、Boost.EnableIf を使用して利用可能なオーバーロード セットからそれらのオーバーロードを削除することができます。オペランドが整数型でない場合。

#include <cstdio>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_integral.hpp>

class CFixed
{
public:
   CFixed( int   ) {}
   CFixed( float ) {}
};

CFixed operator* ( const CFixed& a, const CFixed&  )
{ puts("General CFixed * CFixed"); return a; }

template <class T>
typename boost::enable_if<boost::is_integral<T>, CFixed>::type operator* ( const CFixed& a, T  )
{ puts("CFixed * [integer type]"); return a; }

template <class T>
typename boost::enable_if<boost::is_integral<T>, CFixed>::type operator* ( T , const CFixed& b )
{ puts("[integer type] * CFixed"); return b; }


int main()
{
    CFixed(0) * 10.0f;
    5 * CFixed(20.4f);
    3.2f * CFixed(10);
    CFixed(1) * 100u;
}

当然、別の条件を使用して、これらのオーバーロードを T=int の場合にのみ使用できるようにすることもできます。typename boost::enable_if<boost::is_same<T, int>, CFixed>::type ...

クラスの設計に関しては、おそらくテンプレートにもっと頼ることができます。たとえば、コンストラクターはテンプレートにすることができます。また、整数型と実数型を区別する必要がある場合は、この手法を使用できるはずです。

于 2009-10-22T16:18:19.113 に答える
0

変換を明示的にするのはどうですか?

于 2009-10-22T14:41:30.027 に答える
0

sbiに同意します。必ず、単一パラメーターのコンストラクターを明示的にする必要があります。

ただし、テンプレートを使用して記述したoperator<>関数の急増を回避できます。

template <class T>
CFixed operator* ( const CFixed& a, T b ) 
{ ... } 

template <class T>
CFixed operator* ( T a, const CFixed& b ) 
{ ... } 

関数内のコードに応じて、これは変換をサポートする型でのみコンパイルされます。

于 2009-10-22T14:43:00.623 に答える