Apakah constexpr menyiratkan sebaris?

105

Pertimbangkan fungsi sebaris berikut:

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

dan versi yang setara dengan 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]));
}

Pertanyaan saya adalah: apakah constexprpenspesifikasi menyiratkan inlinepenentu dalam arti bahwa jika argumen non-konstan dilewatkan ke suatu constexprfungsi, kompilator akan mencoba inlinefungsi tersebut seolah-olah inlinepenentu itu dimasukkan ke dalam deklarasinya?

Apakah standar C ++ 11 menjamin itu?

Vincent
sumber
5
'[Akan] kompilator mencoba untuk menyebariskan fungsi' bukanlah apa yang dilakukan oleh inlinepenspesifikasi. (Atau mungkin saya salah memahami ungkapan Anda.)
Luc Danton
5
The inlinespecifier tidak lagi ada hubungannya dengan sebaris
K-ballo
2
Pertanyaan ini didasarkan pada asumsi yang salah yang inlineberhubungan langsung dengan inlining. Jadi tidak ada, yang constexprspecifier tidak berarti inlinespecifier dalam arti bahwa, sebagai yang rasa tidak ada.
Christian Rau

Jawaban:

139

Ya ([dcl.constexpr], §7.1.5 / 2 dalam standar C ++ 11): "fungsi constexpr dan konstruktor constexpr secara implisit sebaris (7.1.2)."

Perhatikan, bagaimanapun, bahwa inlinepenentu benar-benar memiliki sangat sedikit (jika ada) efek pada apakah kompilator akan memperluas fungsi sebaris atau tidak. Namun, hal itu memengaruhi aturan satu definisi, dan dari perspektif itu, kompilator diharuskan mengikuti aturan yang sama untuk suatu constexprfungsi sebagai inlinefungsi.

Saya juga harus menambahkan bahwa terlepas dari constexprmenyiratkan inline, aturan untuk constexprfungsi di C ++ 11 mengharuskannya cukup sederhana sehingga sering kali merupakan kandidat yang baik untuk perluasan sebaris (pengecualian utama adalah yang rekursif). Namun, sejak itu, aturan menjadi semakin longgar, sehingga constexprdapat diterapkan ke fungsi yang jauh lebih besar dan lebih kompleks.

Jerry Coffin
sumber
Mengingat bahwa idenya adalah bahwa ekspresi konstan dievaluasi pada waktu kompilasi, saya kira sebagian besar penggunaan constexprfungsi tidak akan menyebabkan pembuatan kode sama sekali ...
Kerrek SB
11
constexprFungsi @KerrekSB berpotensi dievaluasi pada waktu kompilasi. Namun standar C ++ 14 dikotori dengan standar yang kemungkinan besar akan dipanggil saat runtime. Misalnya:std::array<T,N>::at
Eponymous
@Eponymous ya, tetapi hanya bentuk yang paling dikurangi yang akan tetap sebagai opcode. misalnya: pemeriksaan terikat, akan dievaluasi pada waktu pembuatan, karena jalur kodenya adalah const. Tetapi nilai yang dikembalikan akan menjadi * (data + offset)
v.oddou
16

constexprtidak menyiratkan inlineuntuk variabel non-statis (variabel sebaris C ++ 17)

Meskipun constexprmenyiratkan inlineuntuk fungsi, itu tidak memiliki efek itu untuk variabel non-statis, mengingat variabel sebaris C ++ 17.

Misalnya, jika Anda mengambil contoh minimal yang saya posting di: Bagaimana cara kerja variabel sebaris? dan hapus inline, biarkan saja constexpr, maka variabel mendapat banyak alamat, yang merupakan hal utama yang dihindari variabel sebaris.

constexpr variabel statis bagaimanapun secara implisit statis.

Contoh minimal yang constexprmenyiratkan inlineuntuk fungsi

Seperti disebutkan di: https://stackoverflow.com/a/14391320/895245 efek utama dari inlinetidak sebaris tetapi untuk memungkinkan beberapa definisi fungsi, kutipan standar di: Bagaimana file header C ++ menyertakan implementasi?

Kita dapat mengamati itu dengan memainkan contoh berikut:

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

Kompilasi dan jalankan:

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

Jika kami menghapus inlinedari shared_func, tautan akan gagal dengan:

multiple definition of `shared_func()'

karena tajuk disertakan ke dalam banyak .cppfile.

Tetapi jika kita ganti inlinedengan constexpr, maka berfungsi lagi, karena constexprjuga mengandung arti inline.

GCC mengimplementasikannya dengan menandai simbol sebagai lemah pada file objek ELF: Bagaimana file header C ++ menyertakan implementasi?

Diuji di GCC 8.3.0.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
sumber
3
BTW, variabel anggota kelas statis dinyatakan constexprmasih sebaris. cppreference.com : Variabel anggota statis (tetapi bukan variabel cakupan ruang nama) yang dideklarasikan constexprsecara implisit adalah variabel sebaris.
anton_rh
@anton_rh terima kasih, saya belum melihat aturan itu, perbarui jawaban.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
bukan itu yang dikatakan open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0386r0.pdf . ia mengatakan constexpr menyiratkan sebaris untuk variabel. dengan tidak menyebutkan perbedaan antara ruang lingkup ruang lingkup kelas namespace.
v.oddou