Minimalny działający przykład
Ta niesamowita funkcja C ++ 17 pozwala nam:
main.cpp
#include <cassert>
#include "notmain.hpp"
int main() {
// Both files see the same memory address.
assert(¬main_i == notmain_func());
assert(notmain_i == 42);
}
notmain.hpp
#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP
inline constexpr int notmain_i = 42;
const int* notmain_func();
#endif
notmain.cpp
#include "notmain.hpp"
const int* notmain_func() {
return ¬main_i;
}
Skompiluj i uruchom:
g++ -c -o notmain.o -std=c++17 -Wall -Wextra -pedantic notmain.cpp
g++ -c -o main.o -std=c++17 -Wall -Wextra -pedantic main.cpp
g++ -o main -std=c++17 -Wall -Wextra -pedantic main.o notmain.o
./main
GitHub upstream .
Zobacz także: Jak działają zmienne wbudowane?
Standard C ++ dotyczący zmiennych wbudowanych
Standard C ++ gwarantuje, że adresy będą takie same. C ++ 17 N4659 standardowa wersja robocza
10.1.6 „Specyfikator wbudowany”:
6 Funkcja lub zmienna wbudowana z połączeniem zewnętrznym ma ten sam adres we wszystkich jednostkach tłumaczeniowych.
cppreference https://en.cppreference.com/w/cpp/language/inline wyjaśnia, że jeśli static
nie jest podane, ma link zewnętrzny.
Implementacja zmiennych inline GCC
Możemy obserwować, jak jest realizowany za pomocą:
nm main.o notmain.o
który zawiera:
main.o:
U _GLOBAL_OFFSET_TABLE_
U _Z12notmain_funcv
0000000000000028 r _ZZ4mainE19__PRETTY_FUNCTION__
U __assert_fail
0000000000000000 T main
0000000000000000 u notmain_i
notmain.o:
0000000000000000 T _Z12notmain_funcv
0000000000000000 u notmain_i
i man nm
mówi o u
:
„u” Symbol jest unikalnym symbolem globalnym. To jest rozszerzenie GNU do standardowego zestawu powiązań symboli ELF. Dla takiego symbolu dynamiczny linker upewni się, że w całym procesie jest tylko jeden symbol o tej nazwie i typie w użyciu.
więc widzimy, że jest do tego dedykowane rozszerzenie ELF.
Przed C ++ 17: extern const
Przed C ++ 17, a także w C, możemy osiągnąć bardzo podobny efekt za pomocą extern const
, co doprowadzi do użycia jednej lokalizacji pamięci.
Wady inline
to:
- nie jest możliwe utworzenie zmiennej
constexpr
tą techniką, inline
pozwala tylko na to: Jak zadeklarować constexpr extern?
- jest mniej elegancki, ponieważ musisz zadeklarować i zdefiniować zmienną oddzielnie w nagłówku i pliku cpp
main.cpp
#include <cassert>
#include "notmain.hpp"
int main() {
// Both files see the same memory address.
assert(¬main_i == notmain_func());
assert(notmain_i == 42);
}
notmain.cpp
#include "notmain.hpp"
const int notmain_i = 42;
const int* notmain_func() {
return ¬main_i;
}
notmain.hpp
#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP
extern const int notmain_i;
const int* notmain_func();
#endif
GitHub upstream .
Alternatywy tylko dla nagłówków przed C ++ 17
Nie są one tak dobre, jak extern
rozwiązanie, ale działają i zajmują tylko jedną lokalizację w pamięci:
constexpr
Funkcja, ponieważ constexpr
zakładainline
i inline
pozwala (siły) definicja pojawiać się na każdej jednostki tłumaczeniowej :
constexpr int shared_inline_constexpr() { return 42; }
i założę się, że każdy przyzwoity kompilator wbuduje wywołanie.
Można również użyć const
lub constexpr
statyczną zmienną całkowitą, na przykład:
#include <iostream>
struct MyClass {
static constexpr int i = 42;
};
int main() {
std::cout << MyClass::i << std::endl;
// undefined reference to `MyClass::i'
//std::cout << &MyClass::i << std::endl;
}
ale nie możesz robić takich rzeczy, jak pobieranie jego adresu, bo inaczej zostanie użyty odr, zobacz także: https://en.cppreference.com/w/cpp/language/static "Stałe statyczne składowe " i Definiowanie stałych danych statycznych członków
do
W C sytuacja jest taka sama, jak w C ++ przed C ++ 17, przesłałem przykład pod adresem: Co oznacza „statyczny” w C?
Jedyną różnicą jest to, że w C ++ const
implikuje to static
dla globals, ale nie w C: C ++ semantyka `static const` vs` const`
Jakikolwiek sposób, aby w pełni go wbudować?
DO ZROBIENIA: czy istnieje sposób na pełne wstawienie zmiennej bez użycia jakiejkolwiek pamięci?
Podobnie jak robi to preprocesor.
Wymagałoby to w jakiś sposób:
- zakazanie lub wykrywanie, czy adres zmiennej jest brany
- dodaj te informacje do plików obiektowych ELF i pozwól LTO zoptymalizować je
Związane z:
Testowany w Ubuntu 18.10, GCC 8.2.0.
const
.