Quando devo escrever a palavra-chave inline
para uma função / método em C ++?
Depois de ver algumas respostas, algumas perguntas relacionadas:
Quando não devo escrever a palavra-chave 'inline' para uma função / método em C ++?
Quando o compilador não sabe quando criar uma função / método 'inline'?
Importa se um aplicativo é multithread quando se escreve 'inline' para uma função / método?
c++
inline
one-definition-rule
Parcial
fonte
fonte
inline
(9.3 / 2).Respostas:
Oh cara, uma das minhas irritações.
inline
é maisstatic
ouextern
menos uma diretiva dizendo ao compilador para incorporar suas funções.extern
,static
,inline
São directivas de ligação, usado quase que exclusivamente pelo vinculador, não o compilador.Diz-se que
inline
sugere ao compilador que você acha que a função deve estar embutida. Isso pode ter sido verdade em 1998, mas uma década depois o compilador não precisa dessas dicas. Sem mencionar que os seres humanos geralmente estão errados quando se trata de otimizar código, portanto a maioria dos compiladores ignora a 'dica'.static
- o nome da variável / função não pode ser usado em outras unidades de tradução. O Linker precisa garantir que não use acidentalmente uma variável / função definida estaticamente de outra unidade de tradução.extern
- use este nome de variável / função nesta unidade de tradução, mas não reclame se não estiver definido. O vinculador irá resolvê-lo e garantir que todo o código que tentou usar algum símbolo externo tenha seu endereço.inline
- esta função será definida em várias unidades de tradução, não se preocupe. O vinculador precisa garantir que todas as unidades de conversão usem uma única instância da variável / função.Nota: Geralmente, declarar modelos não
inline
faz sentido, pois eles já têm a semântica de ligaçãoinline
. No entanto, especialização explícita e instanciação de modelos precisaminline
ser usadas.Respostas específicas para suas perguntas:
Somente quando você deseja que a função seja definida em um cabeçalho. Mais exatamente, apenas quando a definição da função pode aparecer em várias unidades de tradução. É uma boa idéia definir funções pequenas (como em um liner) no arquivo de cabeçalho, pois fornece ao compilador mais informações para trabalhar enquanto otimiza seu código. Também aumenta o tempo de compilação.
Não adicione inline apenas porque você acha que seu código será executado mais rapidamente se o compilador o incorporar.
Geralmente, o compilador poderá fazer isso melhor do que você. No entanto, o compilador não tem a opção de incorporar código se não tiver a definição de função. No código otimizado ao máximo, geralmente todos os
private
métodos são incorporados, independentemente de você solicitar ou não.Como um aparte para impedir a inclusão no GCC, use
__attribute__(( noinline ))
e no Visual Studio, use__declspec(noinline)
.O multithreading não afeta o inlining de forma alguma.
fonte
inline
palavra - chave não estão relacionadas. Você tem a idéia certa. Por via de regra, adivinhar o que seria melhorado com a inserção é muito propenso a erros. A exceção a essa regra é um liners.Eu gostaria de contribuir para todas as ótimas respostas neste tópico com um exemplo convincente para dispersar qualquer mal-entendido restante.
Dados dois arquivos de origem, como:
inline111.cpp:
inline222.cpp:
Caso A:
Compilar :
Saída :
Discussão :
Mesmo que você deva ter definições idênticas de suas funções embutidas, o compilador C ++ não o sinaliza se esse não for o caso (na verdade, devido à compilação separada, não há como verificar). É seu próprio dever garantir isso!
O Linker não reclama da regra de definição única , como
fun()
é declaradoinline
. No entanto, como inline111.cpp é a primeira unidade de conversão (que na verdade chamafun()
) processada pelo compilador, o compilador instanciafun()
seu primeiro encontro de chamada no inline111.cpp . Se o compilador decidir não expandirfun()
sua chamada de qualquer outro lugar no seu programa ( por exemplo, de inline222.cpp ), a chamada parafun()
sempre será vinculada à sua instância produzida a partir de inline111.cpp (a chamada parafun()
dentro de inline222.cpptambém pode produzir uma instância nessa unidade de tradução, mas permanecerá desvinculada). De fato, isso é evidente nas&fun = 0x4029a0
impressões idênticas .Por fim, apesar da
inline
sugestão ao compilador de expandir o one-linerfun()
, ele ignora completamente sua sugestão, o que é claro porquefun() = 111
nas duas linhas.Caso B:
Compilar (observe a ordem inversa) :
Saída :
Discussão :
Neste caso, afirma que foram discutidos em Caso A .
Observe um ponto importante: se você comentar a chamada real
fun()
em inline222.cpp ( por exemplo, comentar completamente acout
declaração em inline222.cpp ), então, apesar da ordem de compilação de suas unidades de tradução,fun()
será instanciado no seu primeiro contato em inline111.cpp , resultando na impressão do caso B comoinline111: fun() = 111, &fun = 0x402980
.Caso C:
Compilar (aviso -O2) :
ou
Saída :
Discussão :
-O2
otimização incentiva o compilador a expandir realmente as funções que podem ser incorporadas (observe também que-fno-inline
é o padrão sem as opções de otimização). Como é evidente na cópia impressa aqui,fun()
ela foi realmente expandida em linha (de acordo com sua definição nessa unidade de tradução específica ), resultando em duas impressões diferentesfun()
. Apesar disso, ainda existe apenas uma instância globalmente vinculadafun()
(conforme exigido pelo padrão), como é evidente em impressões idênticas&fun
.fonte
inline
funções sejam um comportamento indefinido..cpp
sendo sua própria unidade de tradução. De preferência, adicione casos para-flto
ativado / desativado.Você ainda precisa alinhar explicitamente sua função ao fazer a especialização de modelo (se a especialização estiver no arquivo .h)
fonte
1) Hoje em dia, praticamente nunca. Se for uma boa ideia incorporar uma função, o compilador fará isso sem a sua ajuda.
2) sempre. Veja # 1.
(Editado para refletir que você dividiu sua pergunta em duas perguntas ...)
fonte
inline
ainda é necessário, por exemplo, para definir uma função em um arquivo de cabeçalho (e isso é necessário para incorporar essa função em várias unidades de compilação).inline
especificador, suas instâncias serão automaticamente recolhidas em uma pelo vinculador e o ODR não será usado.Se a função for declarada no cabeçalho e definida no
.cpp
arquivo, você não deve escrever a palavra-chave.Não existe tal situação. O compilador não pode tornar uma função embutida. Tudo o que pode fazer é alinhar algumas ou todas as chamadas para a função. Não é possível fazer isso se não tiver o código da função (nesse caso, o vinculador precisa fazê-lo, se puder).
Não, isso não importa.
fonte
Isso depende do compilador usado. Não confie cegamente que hoje em dia os compiladores sabem melhor do que os humanos como incorporar e você nunca deve usá-lo por motivos de desempenho, porque é a diretiva de ligação e não a dica de otimização. Embora eu concorde que ideologicamente esses argumentos sejam corretos, encontrar a realidade pode ser uma coisa diferente.
Depois de ler vários threads, tentei, por curiosidade, os efeitos do inline no código que estou trabalhando e os resultados foram que eu obtive uma aceleração mensurável para o GCC e nenhuma aceleração para o compilador Intel.
(Mais detalhes: simulações matemáticas com poucas funções críticas definidas fora da classe, GCC 4.6.3 (g ++ -O3), ICC 13.1.0 (icpc -O3); adicionar inline a pontos críticos causou + 6% de aceleração com o código GCC).
Portanto, se você qualificar o GCC 4.6 como um compilador moderno, o resultado é que a diretiva inline ainda será importante se você escrever tarefas intensivas da CPU e souber exatamente onde está o gargalo.
fonte
Na realidade, praticamente nunca. Tudo o que você está fazendo é sugerir que o compilador faça uma determinada função embutida (por exemplo, substitua todas as chamadas para essa função / com seu corpo). Não há garantias, é claro: o compilador pode ignorar a diretiva.
O compilador geralmente fará um bom trabalho ao detectar + otimizar coisas como esta.
fonte
inline
há uma diferença semântica em C ++ (por exemplo, na maneira como várias definições são tratadas), o que é importante em alguns casos (por exemplo, modelos).Eu verifiquei isso no Visual Studio 9 (15.00.30729.01) compilando com / FAcs e observando o código do assembly: O compilador produziu chamadas para funções-membro sem a otimização habilitada no modo de depuração . Mesmo se a função estiver marcada com __forceinline , nenhum código de tempo de execução embutido será produzido.
fonte
Você deseja colocá-lo no início, antes do tipo de retorno. Mas a maioria dos compiladores ignora. Se ele estiver definido e tiver um bloco de código menor, a maioria dos compiladores o considerará em linha de qualquer maneira.
fonte
A menos que você esteja escrevendo uma biblioteca ou tenha motivos especiais, você pode esquecer
inline
e usar a otimização do tempo do link . Isso elimina o requisito de que uma definição de função deva estar em um cabeçalho para ser considerada como embutida nas unidades de compilação, e é exatamente isso queinline
permite.(Mas consulte Existe algum motivo para não usar a otimização do tempo do link? )
fonte
A palavra-chave inline solicita que o compilador substitua a chamada de função pelo corpo da função, ele primeiro avalia a expressão e depois passa. Reduz a sobrecarga da chamada de função, pois não há necessidade de armazenar o endereço de retorno e a pilha de memória não é necessária para a função. argumentos.
Quando usar:
fonte
inline
ou não C e C ++. C Inline: stackoverflow.com/a/62287072/7194773 C ++ inline: stackoverflow.com/a/62230963/7194773C ++ inline é totalmente diferente de C inline .
inline
por si só afeta o compilador, montador e vinculador. É uma diretiva para o compilador dizendo que apenas emite um símbolo para esta função / dados se for usado na unidade de tradução e, se for, como os métodos de classe, diga ao assembler para armazená-los na seção.section .text.c::function(),"axG",@progbits,c::function(),comdat
ou.section .bss.i,"awG",@nobits,i,comdat
para dados. As instanciações de modelo também entram em seus próprios grupos de comdat.Isto segue
.section name, "flags"MG, @type, entsize, GroupName[, linkage]
. Por exemplo, o nome da seção é.text.c::function()
.axG
significa que a seção é alocável, executável e em um grupo, ou seja, um nome de grupo será especificado (e não há sinalizador M, portanto, nenhum tamanho será especificado);@progbits
significa que a seção contém dados e não está em branco;c::function()
é o nome do grupo e o grupo possuicomdat
linkage, o que significa que em todos os arquivos de objeto, todas as seções encontradas com esse nome de grupo marcadas com comdat serão removidas do executável final, exceto 1, isto é, o compilador garante que haja apenas uma definição na unidade de tradução e, em seguida, pede ao assembler para colocar em seu próprio grupo no arquivo de objeto (1 seção em 1 grupo) e, em seguida, o vinculador garantirá que, se algum arquivo de objeto tiver um grupo com o mesmo nome, inclua apenas um no .exe final. A diferença entre etc pelo montador devido a suas diretivas.inline
e não usarinline
agora é visível para o montador e, como resultado, o vinculador, porque não é armazenado no arquivo regular.data
ou.text
static inline
em uma classe significa que é uma definição de tipo e não declaração (permite que um membro estático seja definido na classe) e torne-o inline; agora se comporta como acima.static inline
no escopo do arquivo afeta apenas o compilador. Isso significa para o compilador: somente emita um símbolo para esta função / dados se for usado na unidade de tradução e faça isso como um símbolo estático comum (armazene em.text /.data sem a diretiva .globl). Para o montador, agora não há diferença entrestatic
estatic inline
extern inline
é uma declaração que significa que você deve definir esse símbolo na unidade de tradução ou gerar erro do compilador; se estiver definido, trate-o como regularinline
e para o montador e vinculador não haverá diferença entreextern inline
einline
, portanto, este é apenas um protetor de compilador.O conjunto acima, sem a linha de erro, é recolhido
inline int i[5]
. Obviamente, se você fezextern inline int i[] = {5};
entãoextern
seria ignorado devido à definição explícita através da atribuição.inline
em um espaço para nome, veja isso e issofonte
Ao desenvolver e depurar código, deixe de
inline
fora. Isso complica a depuração.O principal motivo para adicioná-los é ajudar a otimizar o código gerado. Normalmente, isso troca maior espaço de código por velocidade, mas às vezes
inline
economiza espaço em código e tempo de execução.Gastar esse tipo de pensamento sobre otimização de desempenho antes da conclusão do algoritmo é otimização prematura .
fonte
inline
as funções normalmente não são incorporadas, a menos que sejam compiladas com otimizações, para que não afetem a depuração de forma alguma. Lembre-se de que é uma dica, não uma demanda.inline
tenham impedido, mas as funções foram incorporadas. Era impossível estabelecer um ponto de interrupção significativo neles.inline
não fará nada para melhorar o código em um compilador moderno, que pode descobrir se é incorporado ou não por si próprio.Quando alguém deve alinhar:
1.Quando alguém deseja evitar a sobrecarga de coisas que acontecem quando a função é chamada como passagem de parâmetro, transferência de controle, retorno de controle etc.
2.A função deve ser pequena, frequentemente chamada e tornar inline é realmente vantajosa, pois, de acordo com a regra 80-20, tente tornar inline a função que tenha maior impacto no desempenho do programa.
Como sabemos, o inline é apenas uma solicitação para o compilador semelhante ao registro e isso custará o tamanho do código do objeto.
fonte
inline
perdeu seu status como uma dica de otimização, e a maioria dos compiladores o usa apenas para permitir permissões para várias definições - como o IMO deveria. Mais ainda, desde C ++ 11,register
foi totalmente reprovado por seu significado anterior de 'Eu sei melhor do que o compilador otimizar': agora é apenas uma palavra reservada, sem significado atual.inline
até certo ponto.A função embutida C ++ é um conceito poderoso que é comumente usado com classes. Se uma função estiver embutida, o compilador coloca uma cópia do código dessa função em cada ponto em que a função é chamada em tempo de compilação.
Qualquer alteração em uma função embutida pode exigir que todos os clientes da função sejam recompilados porque o compilador precisaria substituir todo o código novamente, caso contrário, continuará com a funcionalidade antiga.
Para embutir uma função, coloque a palavra-chave embutida antes do nome da função e defina a função antes que sejam feitas chamadas para a função. O compilador pode ignorar o qualificador em linha, caso a função definida seja mais do que uma linha.
Uma definição de função em uma definição de classe é uma definição de função embutida, mesmo sem o uso do especificador embutido.
A seguir, é apresentado um exemplo, que utiliza a função embutida para retornar no máximo dois números
para mais informações veja aqui .
fonte