Czy constexpr oznacza inline?

105

Rozważ następującą wbudowaną funkcję:

// 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]));
}

i odpowiednik w wersji 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]));
}

Moje pytanie brzmi: czy constexprspecyfikator implikuje inlinespecyfikator w tym sensie, że jeśli do constexprfunkcji zostanie przekazany argument inny niż stały , kompilator spróbuje inlinetej funkcji tak, jakby inlinespecyfikator został umieszczony w jej deklaracji?

Czy standard C ++ 11 to gwarantuje?

Vincent
źródło
5
„[Czy] kompilator spróbuje wbudować funkcję” nie jest tym, co inlinerobi specyfikator. (A może źle zrozumiałem twoje sformułowanie.)
Luc Danton
5
Specyfikator inlinenie ma już nic wspólnego z inliningiem
K-ballo
2
Pytanie dotyczy błędnego założenia, które inlinejest bezpośrednio związane z inliningiem. Więc nie, constexprspecyfikator nie implikuje inlinespecyfikatora w tym sensie, ponieważ ten sens nie istnieje.
Christian Rau

Odpowiedzi:

139

Tak ([dcl.constexpr], §7.1.5 / 2 w standardzie C ++ 11): „funkcje constexpr i konstruktory constexpr są niejawnie wbudowane (7.1.2)”.

Należy jednak zauważyć, że inlinespecyfikator naprawdę ma bardzo niewielki (jeśli w ogóle) wpływ na to, czy kompilator prawdopodobnie rozszerzy funkcję wbudowaną, czy nie. Ma to jednak wpływ na regułę jednej definicji iz tej perspektywy kompilator musi przestrzegać tych samych reguł dla constexprfunkcji, co inlinefunkcja.

Powinienem również dodać, że niezależnie od constexprimplikacji inline, reguły dla constexprfunkcji w C ++ 11 wymagały, aby były na tyle proste, że często były dobrymi kandydatami do rozwijania inline (podstawowym wyjątkiem są te, które są rekurencyjne). Jednak od tego czasu reguły stawały się coraz luźniejsze, więc constexprmożna je zastosować do znacznie większych, bardziej złożonych funkcji.

Jerry Coffin
źródło
Biorąc pod uwagę, że idea jest taka, że ​​wyrażenia stałe są obliczane w czasie kompilacji, przypuszczam, że większość zastosowań constexprfunkcji w ogóle nie spowoduje generowania kodu ...
Kerrek SB,
11
constexprFunkcje @KerrekSB są potencjalnie oceniane w czasie kompilacji. Jednak standard C ++ 14 jest zaśmiecony tymi, które najprawdopodobniej będą wywoływane w czasie wykonywania. Na przykład:std::array<T,N>::at
Eponymous
@Eponymous tak, ale tylko najbardziej ograniczona forma pozostanie jako rozkazy. Np .: powiązane kontrole zostaną ocenione w czasie kompilacji, ponieważ ich ścieżka kodu jest stała. Ale zwrócona wartość będzie * (dane + przesunięcie)
v.oddou
16

constexprnie oznacza inlinezmiennych niestatycznych (zmienne wbudowane w C ++ 17)

Chociaż constexprsugeruje to inlinedla funkcji, nie ma tego efektu w przypadku zmiennych niestatycznych, biorąc pod uwagę zmienne wbudowane w C ++ 17.

Na przykład, jeśli weźmiesz minimalny przykład, który opublikowałem pod adresem: Jak działają zmienne wbudowane? i usuń inline, pozostawiając po prostu constexpr, wtedy zmienna otrzymuje wiele adresów, co jest główną rzeczą, której zmienne wbudowane unikają.

constexpr zmienne statyczne są jednak niejawnie statyczne.

Minimalny przykład, który constexprsugeruje inlinedla funkcji

Jak wspomniano na: https://stackoverflow.com/a/14391320/895245, głównym efektem inlinenie jest wstawianie, ale dopuszczenie wielu definicji funkcji, standardowy cytat pod adresem: W jaki sposób plik nagłówkowy C ++ może zawierać implementację?

Możemy to zaobserwować, grając na poniższym przykładzie:

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();
}

Skompiluj i uruchom:

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

Jeśli usuniemy inlinez shared_func, link nie zadziała z:

multiple definition of `shared_func()'

ponieważ nagłówek jest dołączany do wielu .cppplików.

Ale jeśli zastąpimy inlinego constexpr, to znowu działa, ponieważ constexprrównież implikuje inline.

GCC implementuje to, oznaczając symbole jako słabe w plikach obiektowych ELF: W jaki sposób plik nagłówkowy C ++ może zawierać implementację?

Przetestowano w GCC 8.3.0.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
źródło
3
BTW, zadeklarowana zmienna składowa klasy statycznej constexprjest nadal wbudowana. cppreference.com : zadeklarowana statyczna zmienna constexpr
anton_rh
@anton_rh dzięki, nie widziałem tej reguły, zaktualizuj odpowiedź.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
nie tak mówi open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0386r0.pdf . mówi, że constexpr implikuje inline dla zmiennych. bez wzmianki o różnicy między zakresem przestrzeni nazw zakresu klasy.
v.oddou