Quando tento usar float
como parâmetro de modelo, o compilador chora por esse código, embora int
funcione bem.
É porque não posso usar float
como parâmetro de modelo?
#include<iostream>
using namespace std;
template <class T, T defaultValue>
class GenericClass
{
private:
T value;
public:
GenericClass()
{
value = defaultValue;
}
T returnVal()
{
return value;
}
};
int main()
{
GenericClass <int, 10> gcInteger;
GenericClass < float, 4.6f> gcFlaot;
cout << "\n sum of integer is "<<gcInteger.returnVal();
cout << "\n sum of float is "<<gcFlaot.returnVal();
return 0;
}
Erro:
main.cpp: In function `int main()':
main.cpp:25: error: `float' is not a valid type for a template constant parameter
main.cpp:25: error: invalid type in declaration before ';' token
main.cpp:28: error: request for member `returnVal' in `gcFlaot',
which is of non-class type `int'
Estou lendo "Data Structures for Game Programmers", de Ron Penton, o autor passa um float
, mas quando tento parece que não compila.
c++
templates
generics
floating-point
yokks
fonte
fonte
float
como um parâmetro de modelo sem tipo ? Em que capítulo é isso?Respostas:
O padrão C ++ atual não permite
float
(ou seja, número real) ou literais de string de caracteres a serem usados como parâmetros de modelo sem tipo . É claro que você pode usar os tiposfloat
echar *
como argumentos normais.Talvez o autor esteja usando um compilador que não segue o padrão atual?
fonte
template<char ...cs>
, o literal de string pode ser convertido em tal pacote em tempo de compilação. Aqui está uma demonstração do ideone . (Demo é C ++ 14, mas é fácil transportá-lo de volta para C ++ 11 -std::integer_sequence
é a única dificuldade)char &*
como um parâmetro de modelo se definir o literal em outro lugar. Funciona muito bem como uma solução alternativa.A RESPOSTA SIMPLES
O padrão não permite pontos flutuantes como argumentos de modelo não-tipo , que podem ser lidos na seção seguinte do padrão C ++ 11;
Mas .. mas .. POR QUE !?
Provavelmente é devido ao fato de que os cálculos de ponto flutuante não podem ser representados de maneira exata. Se fosse permitido, poderia / resultaria em um comportamento errôneo / estranho ao fazer algo como isso;
Pretendíamos chamar a mesma função duas vezes, mas pode não ser o caso, pois a representação de ponto flutuante dos dois cálculos não é garantida como exatamente a mesma.
Como eu representaria valores de ponto flutuante como argumentos de modelo?
Com
C++11
você poderia escrever algumas expressões constantes bastante avançadas ( constexpr ) que calculariam o numerador / denominador de um tempo de compilação de valor flutuante e então passaria esses dois como argumentos inteiros separados.Lembre-se de definir algum tipo de limite para que os valores de ponto flutuante próximos uns dos outros produzam o mesmo numerador / denominador , caso contrário, é um tanto inútil, pois produzirá o mesmo resultado mencionado anteriormente como um motivo para não permitir valores de ponto flutuante como não-tipo argumentos do modelo .
fonte
<ratio>
, descrita por §20.10 como "aritmética racional em tempo de compilação". O que vai direto ao seu exemplo.<ratio>
?12345 * 12345
é? (Ele faz permitirint
parâmetros do modelo, mesmo que não especifica a largura de um int assinado ou se essa expressão é UB.)Apenas para fornecer um dos motivos pelos quais isso é uma limitação (pelo menos no padrão atual).
Ao combinar as especializações do template, o compilador combina os argumentos do template, incluindo argumentos não-tipo.
Por sua própria natureza, os valores de ponto flutuante não são exatos e sua implementação não é especificada pelo padrão C ++. Como resultado, é difícil decidir quando dois argumentos sem tipo de ponto flutuante realmente correspondem:
Essas expressões não produzem necessariamente o mesmo "padrão de bits" e, portanto, não seria possível garantir que usassem a mesma especialização - sem um texto especial para cobrir isso.
fonte
==
operador :-) Nós já aceitamos esta imprecisão em tempo de execução, por que não em tempo de compilação também?Na verdade, você não pode usar literais float como parâmetros de modelo. Consulte a seção 14.1 ("Um parâmetro de modelo não-tipo deve ter um dos seguintes tipos (opcionalmente qualificado pelo cv) ...") do padrão.
Você pode usar uma referência ao float como um parâmetro de modelo:
fonte
Envolva o (s) parâmetro (s) em sua própria classe como constexprs. Efetivamente, isso é semelhante a uma característica, pois parametriza a classe com um conjunto de flutuadores.
e, em seguida, crie um modelo tomando o tipo de classe como parâmetro
e então usá-lo assim ...
Isso permite que o compilador garanta que apenas uma única instância do código seja criada para cada instanciação do modelo com o mesmo pacote de parâmetros. Isso contorna todos os problemas e você pode usar floats e doubles como constexpr dentro da classe modelada.
fonte
Se você concordar em ter um padrão fixo por tipo, poderá criar um tipo para defini-lo como uma constante e especializá-lo conforme necessário.
Se você tiver o C ++ 11, poderá usar constexpr ao definir o valor padrão. Com C ++ 14, MyTypeDefault pode ser uma variável de modelo que é um pouco mais limpa sintaticamente.
fonte
As outras respostas fornecem boas razões pelas quais você provavelmente não quer parâmetros de modelo de ponto flutuante, mas o verdadeiro travão IMO é que a igualdade usando '==' e igualdade bit a bit não são a mesma:
-0.0 == 0.0
, mas0.0
e-0.0
não são iguais aos bitsNAN != NAN
Nenhum tipo de igualdade é um bom cancelamento para igualdade de tipo: claro, o ponto 2. torna o uso
==
inválido para determinar a igualdade de tipo. Pode-se usar a igualdade bit a bit em vez disso, masx != y
não implica issoMyClass<x>
eMyClass<y>
são tipos diferentes (por 2.), o que seria um tanto estranho.fonte
Você sempre pode fingir ...
Ref: http://code-slim-jim.blogspot.jp/2013/06/c11-no-floats-in-templates-wtf.html
fonte
float
! = Número racional. Os dois são ideias muito distintas. Um é calculado por meio de uma mantissa e um expoente, o outro é, bem, um racional - nem todo valor representável por um racional é representável por afloat
.float
é definitivamente um número racional, mas háfloat
s que não são representáveis como proporções de doisint
s. A mantissa é um inteiro e o expoente 2 ^ é um inteiroSe você não precisa que o duplo seja uma constante de tempo de compilação, pode passá-lo como um ponteiro:
fonte
Começando com C ++ 20, isso é possível .
Isso também dá a resposta à pergunta original:
Porque ninguém o implementou no padrão ainda. Não existe uma razão fundamental.
Em C ++ 20, os parâmetros de template não-tipo agora podem ser flutuantes e até mesmo objetos de classe.
Existem alguns requisitos em objetos de classe (eles devem ser um tipo literal ) e cumprir alguns outros requisitos para excluir os casos patológicos, como operador definido pelo usuário == ( Detalhes ).
Podemos até usar
auto
Observe que o GCC 9 (e 10) implementa parâmetros de template de classe sem tipo, mas não para floats ainda .
fonte
Se você deseja representar apenas uma precisão fixa, pode usar uma técnica como esta para converter um parâmetro float em um int.
Por exemplo, uma matriz com um fator de crescimento de 1,75 poderia ser criada da seguinte maneira, assumindo 2 dígitos de precisão (dividir por 100).
Se você não gosta da representação de 1,75 como 175 na lista de argumentos do modelo, você pode sempre envolvê-lo em alguma macro.
fonte
...::Factor = _Factor_/100.0;
caso contrário, será uma divisão inteira.