C ++ 17 inline
переменные
Если вы думаете, что хотите extern const
, то более вероятно, что вы действительно захотите использовать 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
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 .
Есть ли способ полностью встроить его?
TODO: есть ли способ полностью встроить переменную без использования памяти вообще?
Очень похоже на то, что делает препроцессор.
Это потребует как-то:
- запрещение или обнаружение, если адрес переменной взят
- добавить эту информацию в объектные файлы ELF и позволить LTO оптимизировать ее
Связанный:
Протестировано в Ubuntu 18.10, GCC 8.2.0.