C ++ 17 inline
змінних
Якщо ви ввімкнули "C ++ const statik", то це дуже ймовірно, що ви дійсно хочете використовувати це C ++ 17 вбудовані змінні .
Ця дивовижна функція C ++ 17 дозволяє нам:
- зручно використовувати лише одну адресу пам'яті для кожної константи
- зберігати його як
constexpr
: Як оголосити constexpr extern?
- зробіть це в одному рядку з одного заголовка
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;
}
Складіть і запустіть:
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 вище за течією .
Дивіться також: Як працюють вбудовані змінні?
Стандарт C ++ для вбудованих змінних
Стандарт C ++ гарантує, що адреси будуть однаковими. C ++ 17 N4659 стандартний проект
10.1.6 "Вбудований специфікатор":
6 Вбудована функція або змінна із зовнішнім зв'язком має однакову адресу у всіх одиницях перекладу.
cppreference https://en.cppreference.com/w/cpp/language/inline пояснює, що якщо static
не вказано, то воно має зовнішню зв'язок.
Вбудована змінна версія GCC
Ми можемо спостерігати, як це реалізується за допомогою:
nm main.o notmain.o
який містить:
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
і man nm
говорить про u
:
"u" Символ - це унікальний глобальний символ. Це розширення GNU до стандартного набору прив'язок символу ELF. Для такого символу динамічний лінкер переконається, що у всьому процесі є лише один символ із цим іменем та типом у використанні.
тому ми бачимо, що для цього є виділене розширення ELF.
Pre-C ++ 17: extern const
До C ++ 17 та C, ми можемо досягти дуже подібного ефекту за допомогою a extern const
, що призведе до того, що буде використано єдине місце пам'яті.
Мінуси inline
:
- неможливо зробити змінну за
constexpr
допомогою цієї методики, inline
дозволяє лише : Як оголосити constexpr extern?
- він менш елегантний, оскільки вам доведеться оголошувати та визначати змінну окремо у файлі заголовка та 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 вище за течією .
Замінники заголовка Pre-C ++ 17 лише
Це не так добре, як extern
рішення, але вони працюють і займають лише одне місце пам'яті:
constexpr
Функція, тому що constexpr
має на увазіinline
і inline
дозволяє (сил) визначення з'являтися на кожному ЕП :
constexpr int shared_inline_constexpr() { return 42; }
і я маю надію, що будь-який пристойний компілятор вкладе в дзвінок.
Ви також можете використовувати const
або constexpr
статичну змінну, як у:
#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;
}
але ви не можете робити такі речі, як прийняття його адреси, інакше він стає odr-використаний, див. також: Визначення членів статичних даних constexpr
С
У C ситуація така ж, як у C ++ до C ++ 17, я завантажив приклад за адресою: Що означає "статичний" у C?
Єдина відмінність полягає в тому, що в C ++, const
мається static
на увазі для глобальних, але це не в C: C ++ семантиці `статичного const` проти` const`
Будь-який спосіб його повністю накреслити?
TODO: Чи є спосіб повністю вбудувати змінну, не використовуючи взагалі ніякої пам'яті?
Наче схоже на те, що робить препроцесор.
Для цього потрібно якось:
- заборона або виявлення, чи взята адреса змінної
- додайте цю інформацію до об’єктних файлів ELF, і дозвольте LTO оптимізувати її
Пов'язані:
Випробувано в Ubuntu 18.10, GCC 8.2.0.