Могу ли я смешивать extern и const как extern const? Если да, то определяет ли квалификатор const его господство только в пределах области, в которой он объявлен, или он должен точно соответствовать объявлению единицы перевода, в которой он объявлен? Т.е. могу ли я объявить say extern const int i;
, даже если фактическое i не является константой, и наоборот?
Смешивание extern и const
Ответы (5)
- Да, вы можете использовать их вместе.
- И да, он должен точно соответствовать объявлению в единице перевода, в которой он фактически объявлен. Если, конечно, вы не участвуете в Соревнование по программированию на C "Underhanded C" :-)
Обычный паттерн:
- file.h:
extern const int a_global_var;
- file.c:
#include "file.h"
const int a_global_var = /* some const expression */;
Изменить: включенный комментарий legends2k. Спасибо.
int a_global_var = <some_value_here>;
?
- person legends2k; 03.02.2010
const
s неявно static
, вам нужен extern
даже в определении a_global_var
(в file.c). Без этого все, что включает file.h, не будет ссылаться, потому что ищет const int a_global_var
с внешней связью.
- person ; 25.03.2010
extern
не существует - это правильно. Если константа предварительно объявлена как extern, то extern в определении не является обязательным. Даже без явного extern он определит такой константный объект с внешней связью. - stackoverflow.com/a/2151876/183120
- person legends2k; 22.07.2013
extern
- person patentfox; 30.05.2015
extern const int a_global_var = /* expression */;
- person expoter; 08.03.2016
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
См. Также: Как работают встроенные переменные?
Стандарт 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.
До 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
Альтернативы только для заголовков до 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
Есть ли способ полностью встроить его?
TODO: есть ли способ полностью встроить переменную без использования памяти?
Очень похоже на то, что делает препроцессор.
Это потребует как-то:
- запрещение или обнаружение, если адрес переменной взят
- добавить эту информацию в объектные файлы ELF и позволить LTO оптимизировать ее
Связанный:
Протестировано в Ubuntu 18.10, GCC 8.2.0.
constexpr
, неявно является встроенной переменной. .
- person legends2k; 11.08.2019
inline
имеет проблему, заключающуюся в том, что если другая DLL уже была связана с вашей DLL, и вы изменили только это значение, чем перестроили свою DLL, старая DLL продолжит использовать старое значение. extern const
решает эту проблему, но требует фактического просмотра памяти каждый раз, когда один из ваших ссылочных клиентов запрашивает значение.
- person Keith Russell; 19.11.2019
Вы можете использовать их вместе. Но вам нужно быть последовательным в использовании const, потому что, когда C ++ выполняет оформление имен, const включается в информацию о типе, которая используется для украшения имен символов. поэтому extern const int i
будет относиться к другой переменной, чем extern int i
Если вы не используете extern "C" {}. Оформление имени C не обращает внимания на const.
Вы можете использовать их вместе, и вы можете делать все, что игнорирует ключевое слово const, потому что это все; ключевое слово. Он сообщает компилятору, что вы не будете изменять переменную, что, в свою очередь, позволяет компилятору выполнять некоторые полезные операции и не дает вам изменять то, что вы не хотели.
На Possibility.com есть достойная статья с дополнительной информацией.
Да, вы можете использовать их вместе.
Если вы объявите «extern const int i», то i будет const во всей своей области видимости. Переопределить его как неконстантное невозможно. Конечно, вы можете обойти флаг const, отбросив его (используя const_cast).
extern
сconst
отключит сворачивание констант и заставит компилятор выделить память для константы, чего не было бы в противном случае, при этом он будет выполнять замену на месте (после сворачивания , если возможно). [следовательно, это не рекомендуется, и я решил не использовать его :)] - person legends2k   schedule 20.02.2010extern const
вы не указываете значение инициализации (см. Принятый ответ), и компилятор будет ожидать, что компоновщик заполнит пробелы и тем самым вынудит компоновщик выделить место для константы. - person legends2k   schedule 16.10.2012