Pergunta

Eu tenho uma classe que encapsula um pouco de aritmética, digamos cálculos de ponto fixo. Eu gosto da idéia de sobrecarga de operadores aritméticos, então eu escrever o seguinte:

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

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

Tudo funciona. Eu posso escrever 3 * CFixed (0) e CFixed (3) * 10.0f. Mas agora eu percebo, eu posso implementar operador * com um inteiro operando muito mais eficaz. Então eu sobrecarregá-lo:

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

Ele ainda funciona, mas agora CFixed (0) * 10.0f chamadas versão sobrecarregada, convertendo float para int (e eu esperava que converter float para CFixed). Claro, eu posso sobrecarregar um versões flutuador bem, mas parece uma explosão combinatória de código para mim. Existe alguma solução alternativa (ou estou projetando minha classe errada)? Como posso saber o compilador para chamar versão sobrecarregada do operador * APENAS com ints?

Foi útil?

Solução

Assumindo que você gostaria que a versão especializada para ser escolhido para qualquer tipo integral (e não apenas int em particular, uma coisa que você poderia fazer é oferecer isso como uma função de modelo e usar Boost.EnableIf para remover as sobrecargas a partir do conjunto de sobrecarga disponível, se o operando não é um tipo integral.

#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;
}

Naturalmente, você também pode usar uma condição diferente para fazer essas sobrecargas disponível somente se T = int: typename boost::enable_if<boost::is_same<T, int>, CFixed>::type ...

Como a projetar a classe, talvez você poderia contar com modelos mais. Por exemplo, o construtor pode ser um modelo, e novamente, se você precisar fazer a distinção entre tipos integrais e reais, deve ser possível empregar esta técnica.

Outras dicas

Você deve sobrecarregar com o tipo float também. A conversão de int de tipo especificado pelo utilizador (CFixed) é de prioridade mais baixo do que o embutido flutuante-integrante conversão para float. Portanto, o compilador irá sempre escolher a função com int, a menos que você adicionar a função com float também.

Para mais detalhes, leia 13,3 seção do C ++ 03 standard. Sentir a dor.

Parece que eu perdi a trilha dele também. :-( relatórios UncleBens que a adição de flutuador só não resolver o problema, como a versão com double deve ser adicionado também. mas em qualquer caso, a adição de vários operadores relacionados com built-in tipos é tedioso, mas não resulta em um impulso combinatória.

Se você tem construtores que podem ser invocados com apenas um argumento, você efetivamente criado um operador de conversão implícita. No seu exemplo, sempre que seja necessária uma CFixed, tanto um int e uma float pode ser passado. Este é, naturalmente perigoso, porque o compilador pode gerar código silenciosamente chamando a função errada, em vez de latir para você quando você se esqueceu de incluir a declaração de alguma função.

Portanto, uma boa regra diz que, sempre que você está construtores de escrita que podem ser chamados com apenas um argumento (note que este foo(int i, bool b = false) pode ser chamado com um argumento, também, mesmo que ele usa dois argumentos), você deve fazer com que explicit construtor, a menos que você realmente quer conversão implícita para chutar. construtores explicit não são usados ??pelo compilador para conversões implícitas.

Você teria que mudar sua classe para o seguinte:

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

Eu descobri que há muito poucas excepções a esta regra. (std::string::string(const char*) é uma vez famoso.)

Editar: me desculpe, eu perdi o ponto sobre não permitir conversões implícitas de int para float.

A única maneira que vejo para evitar isso é fornecer aos operadores para float também.

Que tal fazer a conversão explícita ?

Concordo com a SBI, você definitivamente deve fazer seus construtores de parâmetro único explícito.

Você pode evitar uma explosão no operador <> funções que você escreve com modelos, no entanto:

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

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

Dependendo do que o código está nas funções, isto só irá compilar com tipos que você apoia conversão de.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top