Constexpr implica inline?

105

Considere a seguinte função embutida:

// Inline specifier version
#include<iostream>
#include<cstdlib>

inline int f(const int x);

inline int f(const int x)
{
    return 2*x;
}

int main(int argc, char* argv[])
{
    return f(std::atoi(argv[1]));
}

e a versão equivalente constexpr:

// Constexpr specifier version
#include<iostream>
#include<cstdlib>

constexpr int f(const int x);

constexpr int f(const int x)
{
    return 2*x;
}

int main(int argc, char* argv[])
{
    return f(std::atoi(argv[1]));
}

Minha pergunta é: o constexprespecificador implica o inlineespecificador no sentido de que se um argumento não constante for passado para uma constexprfunção, o compilador tentará inlinea função como se o inlineespecificador tivesse sido colocado em sua declaração?

O padrão C ++ 11 garante isso?

Vincent
fonte
5
'[Irá] o compilador tentar embutir a função' não é o que o inlineespecificador faz. (Ou talvez eu não tenha entendido sua frase.)
Luc Danton
5
O inlineespecificador não tem mais nada a ver com inlining
K-ballo
2
A questão baseia-se na suposição errada que inlineestá diretamente relacionada ao inlining. Portanto, não, o constexprespecificador não implica o inlineespecificador nesse sentido, pois esse sentido não existe.
Christian Rau

Respostas:

139

Sim ([dcl.constexpr], §7.1.5 / 2 no padrão C ++ 11): "funções constexpr e construtores constexpr são implicitamente embutidos (7.1.2)."

Observe, entretanto, que o inlineespecificador realmente tem muito pouco (se houver) efeito sobre a probabilidade de um compilador expandir uma função embutida ou não. No entanto, isso afeta a regra de definição única e, dessa perspectiva, o compilador deve seguir as mesmas regras para uma constexprfunção como uma inlinefunção.

Devo também acrescentar que, independentemente de constexprimplicar inline, as regras para constexprfunções em C ++ 11 exigiam que fossem simples o suficiente para que fossem frequentemente boas candidatas para expansão sequencial (a principal exceção sendo aquelas que são recursivas). Desde então, no entanto, as regras ficaram progressivamente mais flexíveis, constexprpodendo ser aplicadas a funções substancialmente maiores e mais complexas.

Jerry Coffin
fonte
Dado que a ideia é que as expressões constantes são avaliadas em tempo de compilação, suponho que a maioria dos usos de constexprfunções não causará nenhuma geração de código ...
Kerrek SB
11
As constexprfunções @KerrekSB são potencialmente avaliadas em tempo de compilação. No entanto, o padrão C ++ 14 está repleto de outros que muito provavelmente serão chamados em tempo de execução. Por exemplo:std::array<T,N>::at
Homônimo de
@Eponymous yes mas apenas a forma mais reduzida permanecerá como opcodes. Ex: as verificações de limite serão avaliadas no momento da construção, uma vez que seu caminho de código é constante. Mas o valor retornado será * (dados + deslocamento)
v.oddou
16

constexprnão implica inlinepara variáveis ​​não estáticas (variáveis ​​embutidas C ++ 17)

Embora constexprimplique inlinepara funções, não tem esse efeito para variáveis ​​não estáticas, considerando as variáveis ​​inline do C ++ 17.

Por exemplo, se você pegar o exemplo mínimo que postei em: Como funcionam as variáveis ​​embutidas? e remove o inline, deixando apenas constexpr, então a variável obtém vários endereços, que é a principal coisa que as variáveis ​​embutidas evitam.

constexpr variáveis ​​estáticas são, entretanto, implicitamente estáticas.

Exemplo mínimo que constexprimplica inlinefunções

Conforme mencionado em: https://stackoverflow.com/a/14391320/895245, o principal efeito de inlinenão é embutir, mas permitir várias definições de uma função, citação padrão em: Como um arquivo de cabeçalho C ++ pode incluir implementação?

Podemos observar isso brincando com o seguinte exemplo:

main.cpp

#include <cassert>

#include "notmain.hpp"

int main() {
    assert(shared_func() == notmain_func());
}

notmain.hpp

#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP

inline int shared_func() { return 42; }
int notmain_func();

#endif

notmain.cpp

#include "notmain.hpp"

int notmain_func() {
    return shared_func();
}

Compile e execute:

g++ -c -ggdb3  -O0 -Wall -Wextra -std=c++11 -pedantic-errors  -o 'notmain.o' 'notmain.cpp' 
g++ -c -ggdb3  -O0 -Wall -Wextra -std=c++11 -pedantic-errors  -o 'main.o' 'main.cpp' 
g++ -ggdb3  -O0 -Wall -Wextra -std=c++11 -pedantic-errors  -o 'main.out' notmain.o main.o
./main.out

Se removermos inlinede shared_func, o link falhará com:

multiple definition of `shared_func()'

porque o cabeçalho é incluído em vários .cpparquivos.

Mas se substituirmos inlinepor constexpr, ele funcionará novamente, porque constexprtambém implica inline.

O GCC implementa isso marcando os símbolos como fracos nos arquivos de objeto ELF: Como um arquivo de cabeçalho C ++ pode incluir a implementação?

Testado em GCC 8.3.0.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fonte
3
BTW, uma variável de membro de classe estática declarada constexprainda está embutida. cppreference.com : Uma variável de membro estático (mas não uma variável de escopo de namespace) declarada constexpré implicitamente uma variável embutida.
anton_rh
@anton_rh obrigado, eu não tinha visto essa regra, atualize a resposta.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
não é o que open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0386r0.pdf diz. diz que constexpr implica inline para variáveis. sem menção de uma diferença entre o escopo do namespace e o escopo da classe.
v.oddou