C ++ 17 inline
переменные
Если вы гуглили "C ++ const static", то очень вероятно, что вы действительно хотите использовать 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 upstream .
См. Также: Как работают встроенные переменные?
Стандарт C ++ для встроенных переменных
Стандарт C ++ гарантирует, что адреса будут одинаковыми. C ++ 17 N4659 стандартная тяга
10.1.6 «Встроенный спецификатор»:
6 Встроенная функция или переменная с внешней связью должны иметь одинаковый адрес во всех единицах перевода.
cppreference https://en.cppreference.com/w/cpp/language/inline объясняет, что если static
не задано, то оно имеет внешнюю связь.
Реализация встроенной переменной
Мы можем наблюдать, как это реализовано с помощью:
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 мы можем добиться очень похожего эффекта с 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 upstream .
C
В C ситуация такая же, как в C ++ до C ++ 17, я загрузил пример по адресу: Что означает «статический» в C?
Единственное отличие состоит в том, что в C ++ const
подразумевает static
для глобалов, но не в C: C ++ семантике `static const` против` const`
Есть ли способ полностью встроить его?
TODO: есть ли способ полностью встроить переменную без использования памяти вообще?
Очень похоже на то, что делает препроцессор.
Это потребует как-то:
- запрещение или обнаружение, если адрес переменной взят
- добавить эту информацию в объектные файлы ELF и позволить LTO оптимизировать ее
Связанный:
Протестировано в Ubuntu 18.10, GCC 8.2.0.