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