Смешивание extern и const - PullRequest
       22

Смешивание extern и const

50 голосов
/ 03 февраля 2010

Можно ли смешивать extern и const, как extern const ? Если да, то определяет ли спецификатор const свое правление только в той области, в которой он объявлен, или он должен точно соответствовать объявлению переводческой единицы, в которой он объявлен? То есть Могу ли я объявить, скажем extern const int i;, даже если фактическое i не является константой и наоборот?

Ответы [ 5 ]

50 голосов
/ 03 февраля 2010
  • Да, вы можете использовать их вместе.
  • И да, оно должно точно соответствовать объявлению в единице перевода, в которой оно фактически объявлено. Если, конечно, вы не участвуете в конкурсе закулисного программирования C : -)

Обычный шаблон:

  • file.h:
    extern const int a_global_var;
  • file.c:
    #include "file.h"
    const int a_global_var = /* some const expression */;

Редактировать: Объединенный комментарий legends2k. Спасибо.

5 голосов
/ 03 февраля 2010

Вы можете использовать их вместе. Но вы должны быть последовательны при использовании const, потому что, когда C ++ выполняет оформление имен, const включается в информацию о типе, которая используется для оформления имен символов. поэтому extern const int i будет ссылаться на переменную, отличную от extern int i

Если вы не используете extern "C" {}. В названии украшения не обращают внимания на const.

1 голос

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(&notmain_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 &notmain_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(&notmain_i == notmain_func());
    assert(notmain_i == 42);
}

notmain.cpp

#include "notmain.hpp"

const int notmain_i = 42;

const int* notmain_func() {
    return &notmain_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.

1 голос
/ 03 февраля 2010

Вы можете использовать их вместе, и вы можете делать все виды вещей, которые игнорируют ключевое слово const, потому что это все, что есть;ключевое словоОн сообщает компилятору, что вы не будете изменять переменную, что, в свою очередь, позволяет компилятору выполнять некоторые полезные действия и мешает вам изменить то, что вы не хотели.приличная статья с еще большим фоном.

0 голосов
/ 03 февраля 2010

Да, вы можете использовать их вместе.

Если вы объявляете "extern const int i", то i является const в полном объеме Невозможно переопределить его как неконстантный. Конечно, вы можете обойти флаг const, отбросив его (используя const_cast).

...