Language linkage
Обеспечивает связь между программными модулями, написанными на разных языках программирования.
|
Это также может использоваться для отделения объявления от его модуля. См. Владение модулем . |
(since C++20) |
extern
string-literal
{
declaration-seq
(необязательно)
}
|
(1) | ||||||||
extern
string-literal
declaration
|
(2) | ||||||||
| string-literal | - | невычисляемый строковый литерал , который указывает требуемую языковую связь |
| declaration-seq | - | последовательность объявлений, которая может включать вложенные спецификации связывания |
| declaration | - | объявление |
Содержание |
Объяснение
Каждый тип функции, каждое имя функции с внешней линковкой , и каждое имя переменной с внешней линковкой , обладает свойством, называемым языковая линковка . Языковая линковка инкапсулирует набор требований, необходимых для линковки с программным модулем, написанным на другом языке программирования: соглашение о вызовах , алгоритм манглинга имён (декорирование имён) и т.д.
Гарантированно поддерживаются только две языковые связи:
- "C++" , языковая связь по умолчанию.
- "C" , которая позволяет связываться с функциями, написанными на языке программирования C, и определять в программе на C++ функции, которые могут вызываться из модулей, написанных на C.
extern "C" { int open(const char *path_name, int flags); // Объявление C-функции } int main() { int fd = open("test.txt", 0); // вызов C-функции из C++ программы } // Эта C++ функция может быть вызвана из C кода extern "C" void handler(int) { std::cout << "Callback invoked\n"; // Может использовать C++ }
Поскольку языковая линковка является частью каждого типа функции, указатели на функции также сохраняют языковую линковку. Языковая линковка типов функций (которая представляет соглашение о вызове) и языковая линковка имён функций (которая представляет манглинг имён) независимы друг от друга:
extern "C" void f1(void(*pf)()); // объявляет функцию f1 с C-линковкой, // которая возвращает void и принимает указатель на C-функцию // которая возвращает void и не принимает параметров extern "C" typedef void FUNC(); // объявляет FUNC как тип C-функции, возвращающей void // и не принимающей параметров FUNC f2; // имя f2 имеет C++-линковку, но его тип - C-функция extern "C" FUNC f3; // имя f3 имеет C-линковку и его тип - C-функция void() void (*pf2)(FUNC*); // имя pf2 имеет C++-линковку, и его тип - // "указатель на C++ функцию, которая возвращает void и принимает один // аргумент типа 'указатель на C-функцию, которая возвращает void // и не принимает параметров'" extern "C" { static void f4(); // имя функции f4 имеет внутреннюю линковку (без языка) // но тип функции имеет C-языковую линковку }
Если два объявления одной сущности задают ей различные языковые связывания, программа является некорректной; диагностическое сообщение не требуется, если ни одно объявление не достижимо из другого. Повторное объявление сущности без спецификации связывания наследует языковое связывание сущности и её типа (если он существует).
extern "C" int f(); extern "C++" int f(); // Ошибка: различные языковые связи extern "C" int g(); int g(); // OK, имеет языковую связь C int h(); // по умолчанию имеет языковую связь C++ extern "C" int h(); // Ошибка: различные языковые связи
Особые правила для "C" линковки
Когда члены класса , дружественные функции с завершающей requires clause , (since C++20) или нестатические функции-члены появляются в блоке языка "C" , связывание их типов остается "C++" (но типы параметров, если они есть, остаются "C" ):
extern "C" { class X { void mf(); // функция mf и её тип имеют связывание C++ void mf2(void(*)()); // функция mf2 имеет связывание C++; // параметр имеет тип "указатель на C-функцию" }; } template<typename T> struct A { struct B; }; extern "C" { template<typename T> struct A<T>::B { friend void f(B*) requires true {} // связывание C игнорируется }; } namespace Q { extern "C" void f(); // не является ошибочным }
Пусть
C
будет объявлением, которое объявляет функцию или переменную с языковой линковкой
"C"
. Если другое объявление
D
объявляет сущность с тем же именем и удовлетворяет любому из следующих условий,
C
и
D
объявляют одну и ту же сущность:
-
Dобъявляет переменную, принадлежащую глобальной области видимости. -
Если
Cобъявляет переменную,Dтакже объявляет переменную. -
Если
Cобъявляет функцию,Dтакже объявляет функцию.
В отличие от
обычных переобъявлений
,
C
и
D
могут иметь различные
целевые области видимости
:
extern "C" { int x; int f(); int g() { return 1; } } namespace A { int x; // Ошибка: переопределение "x" int f(); // OK, повторное объявление "f" int g() { return 1; } // Ошибка: переопределение "g" }
Однако ограничения таких объявлений всё ещё применяются, что означает, что они должны либо объявлять функции, либо объявлять переменные, и объявляемые сущности должны иметь одинаковый тип:
namespace A { extern "C" int x(); extern "C" int y(); } int x; // Ошибка: переобъявляет «x» как сущность другого типа namespace B { void y(); // Ошибка: переобъявляет «y» с другим типом }
Примечания
Спецификации языка могут появляться только в области видимости пространства имён .
Фигурные скобки спецификации языка не создают область видимости.
Когда спецификации языка вкладываются друг в друга, действующей является самая внутренняя спецификация.
Объявление, непосредственно содержащееся в спецификации языковой привязки, рассматривается как если бы оно содержало extern спецификатор для целей определения линковки объявленного имени и того, является ли оно определением .
extern "C" int x; // объявление, но не определение // Вышестоящая строка эквивалентна extern "C" { extern int x; } extern "C" { int x; } // объявление и определение extern "C" double f(); static double f(); // ошибка: конфликт линковки extern "C" static void g(); // ошибка: конфликт линковки
extern "C" позволяет включать заголовочные файлы с объявлениями функций библиотеки C в программу на C++, но если тот же заголовочный файл используется в программе на C, extern "C" (что не разрешено в C) должен быть скрыт с помощью соответствующего #ifdef , обычно __cplusplus :
#ifdef __cplusplus extern "C" int foo(int, int); // компилятор C++ видит это #else int foo(int, int); // компилятор C видит это #endif
Единственным современным компилятором, который различает типы функций с языковыми связями "C" и "C++" , является Oracle Studio; остальные не допускают перегрузок, различающихся только языковой связью, включая наборы перегрузок, требуемые стандартом C++ ( std::qsort , std::bsearch , std::signal , std::atexit и std::at_quick_exit ): GCC bug 2316 , Clang bug 6277 , CWG issue 1555 .
extern "C" using c_predfun = int(const void*, const void*); extern "C++" using cpp_predfun = int(const void*, const void*); // некорректно, но принимается большинством компиляторов static_assert(std::is_same<c_predfun, cpp_predfun>::value, "Связи языков C и C++ не должны дифференцировать типы функций."); // следующие объявления не создают перегрузки в большинстве компиляторов // поскольку c_predfun и cpp_predfun считаются одним и тем же типом void qsort(void* base, std::size_t nmemb, std::size_t size, c_predfun* compar); void qsort(void* base, std::size_t nmemb, std::size_t size, cpp_predfun* compar);
Ключевые слова
Отчеты о дефектах
Следующие отчеты об изменениях в поведении, являющиеся дефектными, были применены ретроактивно к ранее опубликованным стандартам C++.
| DR | Применяется к | Поведение в опубликованной версии | Корректное поведение |
|---|---|---|---|
| CWG 4 | C++98 | имена с внутренней линковкой могут иметь языковые линковки | ограничено именами с внешней линковкой |
| CWG 341 | C++98 |
функция с языковой линковкой
"C"
может
иметь то же имя, что и глобальная переменная |
программа является некорректной в этом случае
(диагностика не требуется, если они появляются в разных единицах трансляции) |
| CWG 564 | C++98 |
программа была некорректной, если два объявления
отличались только спецификациями языковой линковки (т.е. разными строковыми литералами после 'extern') |
сравниваются фактические языковые линковки,
указанные в объявлениях |
| CWG 2460 | C++20 |
дружественные функции с завершающей
requires
клаузой
и языковой линковкой "C" имели конфликтующие поведения |
"C"
языковая линковка
игнорируется в этом случае |
| CWG 2483 | C++98 |
линковка типов статических функций-членов
в блоках с языковой линковкой "C" была "C++" |
линковка является "C" |
Ссылки
- Стандарт C++23 (ISO/IEC 14882:2024):
-
- 9.11 Спецификации связывания [dcl.link]
- Стандарт C++20 (ISO/IEC 14882:2020):
-
- 9.11 Спецификации связывания [dcl.link]
- Стандарт C++17 (ISO/IEC 14882:2017):
-
- 10.5 Спецификации связывания [dcl.link]
- Стандарт C++14 (ISO/IEC 14882:2014):
-
- 7.5 Спецификации связывания [dcl.link]
- Стандарт C++11 (ISO/IEC 14882:2011):
-
- 7.5 Спецификации связывания [dcl.link]
- Стандарт C++03 (ISO/IEC 14882:2003):
-
- 7.5 Спецификации связывания [dcl.link]
- Стандарт C++98 (ISO/IEC 14882:1998):
-
- 7.5 Спецификации связывания [dcl.link]