Se eu tiver duas variáveis de membros constantes diferentes, que precisam ser inicializadas com base na mesma chamada de função, existe uma maneira de fazer isso sem chamar a função duas vezes?
Por exemplo, uma classe de fração em que numerador e denominador são constantes.
int gcd(int a, int b); // Greatest Common Divisor
class Fraction {
public:
// Lets say we want to initialize to a reduced fraction
Fraction(int a, int b) : numerator(a/gcd(a,b)), denominator(b/gcd(a,b))
{
}
private:
const int numerator, denominator;
};
Isso resulta em perda de tempo, pois a função GCD é chamada duas vezes. Você também pode definir um novo membro da classe,gcd_a_b
, e primeiro atribuir a saída do gcd à da lista de inicializadores, mas isso levaria à perda de memória.
Em geral, existe uma maneira de fazer isso sem chamadas de função desperdiçadas ou memória? Você pode criar variáveis temporárias em uma lista de inicializadores? Obrigado.
-O3
. Mas, provavelmente, para qualquer implementação de teste simples, ele realmente alinharia a chamada de função. Se você usar__attribute__((const))
ou purificar o protótipo sem fornecer uma definição visível, ele deve permitir que o GCC ou o clang façam a eliminação da subexpressão comum (CSE) entre as duas chamadas com o mesmo argumento. Observe que a resposta de Drew funciona mesmo para funções não puras, por isso é muito melhor e você deve usá-la sempre que a função não estiver alinhada.Respostas:
Sim. Isso pode ser feito com um construtor de delegação , introduzido no C ++ 11.
Um construtor de delegação é uma maneira muito eficiente de adquirir valores temporários necessários para a construção antes que qualquer variável membro seja inicializada.
fonte
.h
), mesmo que a definição real do construtor não seja visível para embutir. isto é, agcd()
chamada seria incorporada em cada site de chamada do construtor e deixaria apenas umcall
para o construtor privado de 3 operandos.Os vars de membros são inicializados pela ordem em que são declarados na decleração de classe, portanto, você pode fazer o seguinte (matematicamente)
Não há necessidade de chamar outros construtores ou até mesmo fazê-los.
fonte
Fraction(a,b,gcd(a,b))
delegação no chamador, levando a um custo total menor. Esse inlining é mais fácil para o compilador do que desfazer a divisão extra nisso. Eu não tentei no godbolt.org, mas você poderia, se estiver curioso. Use gcc ou clang-O3
como uma compilação normal usaria. (C ++ é desenhado em torno do pressuposto de um compilador otimizado moderno, por conseguinte, apresenta comoconstexpr
)@Drew Dormann deu uma solução semelhante ao que eu tinha em mente. Como o OP nunca menciona não poder modificar o ctor, isso pode ser chamado com
Fraction f {a, b, gcd(a, b)}
:Só assim, não há segunda chamada para uma função, construtora ou outra, portanto não há perda de tempo. E não é uma perda de memória, já que um temporário teria que ser criado de qualquer maneira, então você também pode fazer bom uso dele. Também evita uma divisão extra.
fonte
const
, mas pelo menos funciona para outros tipos. E que divisão extra você "também" está evitando? Você quer dizer a resposta do asmmo?,gcd(foo, bar)
é um código extra que poderia e, portanto, deveria ser fatorado em todos os sites de chamada na fonte . Esse é um problema de manutenção / legibilidade, não de desempenho. O compilador provavelmente o incluirá no momento da compilação, o que você deseja para o desempenho.Fraction f( x+y, a+b );
Para escrever do seu jeito, você teria que escreverBadFraction f( x+y, a+b, gcd(x+y, a+b) );
ou usar tmp vars. Ou, pior ainda, e se você quiser escreverFraction f( foo(x), bar(y) );
- então você precisaria que o site de chamada declarasse alguns tmp vars para manter os valores de retorno ou chame essas funções novamente e espere que o compilador as expire, o que estamos evitando. Deseja depurar o caso de um chamador misturando os argumentos paragcd
que não seja realmente o GCD dos dois primeiros argumentos passados para o construtor? Não? Então não torne possível esse bug.