Gerenciando implícita Conversão de tipo em C ++
-
20-08-2019 - |
Pergunta
Eu estou trabalhando em código que faz consultas de vizinho mais próximo. Há duas ideias simples que são a base como uma consulta pode usuário para dados de uma pesquisa:
- mais próximo N pontos para um determinado ponto no espaço.
- todos os pontos dentro de uma determinada distância.
No meu código, os pontos são colocados em um pointList, eo pointList é um recipiente tem a tarefa de manter o controle dos pontos que foram encontrados na pesquisa.
Agora meu objeto pointList tem um construtor:
PointList( unsigned int maxvals ); // #1
Os próximos dois construtores que eu gostaria de adicionar são os seguintes:
PointList( float maxdist ); // #2
PointList( unsigned int maxvals, float maxdist ); // #3
A minha pergunta é: como posso garantir que meus usuários e o compilador C ++ irá gerar o direito construtor para a pointList e diferencia entre construtores 1 e 2? Devo apenas implementar # 3 e fornecem constantes que definem arbitrária grande valores para maxvals e maxdist? Outra alternativa poderia ser a de escrever um outro sistema de objetos leves que regem a lógica para adicionar pontos à lista, mas que se sente como um exagero para uma idéia tão simples.
Eu estou realmente tentando fazer isso transparente para meus usuários, que são na sua maioria cientistas que aprenderam C ++, por vezes, sem o benefício da educação formal. Obrigado!
Solução
Resolução de sobrecarga para tipos inteiros acontecer em duas categorias, o que pode ser muito grosseiramente resumida a
- Promoção: Esta é uma conversão de tipos menores que
int
paraint
ouunsigned int
, dependendo seint
pode armazenar todos os valores do tipo de fonte. - Conversão:. Esta é uma conversão de qualquer tipo inteiro para outro tipo inteiro
Semelhante, a conversão para tipos de ponto flutuante acontecer em duas categorias
- Promoção: Esta é uma conversão de
float
paradouble
- Conversão: Esta é uma conversão a partir de qualquer tipo de ponto flutuante para outro tipo de ponto flutuante
E há uma conversão de inteiro para flutuante ou de volta. Este é classificado como uma conversão, em vez de uma promoção. Uma promoção está classificada melhor do que uma conversão, e em que é necessária apenas uma promoção, nesse caso, será preferido. Assim, você pode usar os seguintes construtores
PointList( int maxVals );
PointList( unsigned int maxVals );
PointList( long maxVals );
PointList( unsigned long maxVals );
PointList( double maxDist );
PointList( long double maxDist );
Para qualquer tipo integer, este deve selecionar o primeiro grupo de construtor. E para qualquer tipo de ponto flutuante, este deve selecionar o segundo grupo de construtores. Seus dois originais construtores poderia facilmente resultar em uma ambigüidade entre float
e unsigned int
, se você passar um int
, por exemplo. Por outro lado, dois construtor argumento, você pode ir com sua solução, se quiser.
Dito isto, gostaria de usar uma função de fábrica também, porque decidir sobre o tipo o significado do parâmetro é bastante frágil que eu penso. A maioria das pessoas seria de esperar o seguinte resultado para igualar
PointList p(floor(1.5));
PointList u((int)1.5);
Mas isso resultaria em um estado diferente de coisas.
Outras dicas
Por que não usar métodos de fábrica em vez de construtores? métodos de fábrica têm a vantagem de nomes customizáveis.
static PointList createNearestValues(unsigned int maxvals) {}
static PointList createByDistance(float maxdist) {}
Considere o uso verdadeiros typedefs . É um pouco mais de esforço por parte do seu código de cliente, mas você é exatidão garantida.
Chamada pointList (10) para o primeiro e pointList (10f) para o segundo.
Para o segundo, você também pode usar 10.0.
Se construtores # 1 e # 2 estão presentes o construtor correto será chamado se o valor que você inserir é de float ou int e nenhuma conversão deve ocorrer. Então, basta garantir que você faça os tipos de números que você usa para chamar explícita (ou seja, 1f e 1). O construtor # 3 não parecem ser muito de uma opção, uma vez que não é realmente necessário e que os usuários apenas confundir as de seu código. Se você precisar de valores padrão para qualquer número que você poderia usar
PointList(int max, float max=VALUE)
e
PointList(float max, int max=VALUE)
Mais uma vez: isso parece fazer mais mal que o código em termos de legibilidade do código
.Este está chamando para uma boa leitura em Overload resolução .
Eu definitivamente utilizar explícitas construtores. No exemplo, o inteiro sem sinal não converter implicitamente.
class A
{
public:
explicit A(float f){}
explicit A(int i){}
};
void test(){
unsigned int uinteger(0);
A a1(uinteger); //Fails, does not allow implicit conversions
A a2((float)uinteger); //OK, explicit conversion
float f(0.0);
A a3(f); //OK
int integer(0);
A a4(integer); //OK
}
A mensagem de erro é bastante fácil de entender:
: error C2668: 'A::A' : ambiguous call to overloaded function
: could be 'A::A(int)'
: or 'A::A(float)'