Языковая связь
Обеспечивает связь между программными единицами, написанными на разных языках программирования.
| Это также можно использовать, чтобы отсоединить объявление от его модуля. См. Принадлежность модулю. | (начиная с 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 function declaration
}
int main()
{
int fd = open("test.txt", 0); // calls a C function from a C++ program
}
// This C++ function can be called from C code
extern "C" void handler(int)
{
std::cout << "Callback invoked\n"; // It can use C++
}
Поскольку языковая связь является свойством каждого типа функции, указатели на функции также сохраняют языковую связь. Языковая связь типов функций (определяющая соглашение о вызовах) и языковая связь имён функций (определяющая искажение имён) независимы друг от друга:
extern "C" void f1(void(*pf)()); // declares a function f1 with C linkage,
// which returns void and takes a pointer to a C function
// which returns void and takes no parameters
extern "C" typedef void FUNC(); // declares FUNC as a C function type that returns void
// and takes no parameters
FUNC f2; // the name f2 has C++ linkage, but its type is C function
extern "C" FUNC f3; // the name f3 has C linkage and its type is C function void()
void (*pf2)(FUNC*); // the name pf2 has C++ linkage, and its type is
// "pointer to a C++ function which returns void and takes one
// argument of type 'pointer to the C function which returns void
// and takes no parameters'"
extern "C"
{
static void f4(); // the name of the function f4 has internal linkage (no language)
// but the function's type has C language linkage
}
Если два объявления одной сущности задают для неё разные варианты языковой связи, программа некорректна; диагностика не требуется, если ни одно из объявлений недоступно из другого. Повторное объявление сущности без спецификации языковой связи наследует языковую связь сущности и её типа (если он есть).
extern "C" int f(); extern "C++" int f(); // Error: different language linkages extern "C" int g(); int g(); // OK, has C language linkage int h(); // has C++ language linkage by default extern "C" int h(); // Error: different language linkages
Особые правила для связи "C"
Когда члены классов, дружественные функции с завершающим предложением requires,(начиная с C++20) или нестатические функции-члены находятся в блоке языковой связи "C", языковая связь их типов остаётся "C++" (но типы параметров, если они есть, остаются "C"):
extern "C"
{
class X
{
void mf(); // the function mf and its type have C++ language linkage
void mf2(void(*)()); // the function mf2 has C++ language linkage;
// the parameter has type “pointer to C function”
};
}
template<typename T>
struct A { struct B; };
extern "C"
{
template<typename T>
struct A<T>::B
{
friend void f(B*) requires true {} // C language linkage ignored
};
}
namespace Q
{
extern "C" void f(); // not ill-formed
}
Пусть 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; // Error: redefines “x”
int f(); // OK, redeclares “f”
int g() { return 1; } // Error: redefines “g”
}
Однако к таким объявлениям по-прежнему применяются ограничения, а это означает, что они должны объявлять либо обе функции, либо обе переменные, и объявленные сущности должны иметь один и тот же тип:
namespace A
{
extern "C" int x();
extern "C" int y();
}
int x; // Error: redeclares “x” as a different kind of entity
namespace B
{
void y(); // Error: redeclares “y” with a different type
}
Примечания
Спецификации языковой связи могут находиться только в области видимости пространства имён.
Фигурные скобки спецификации языковой связи не создают область видимости.
При вложении спецификаций языковой связи действует самая внутренняя спецификация.
Для определения связи объявленного имени и того, является ли оно определением, объявление, непосредственно содержащееся в спецификации языковой связи, рассматривается так, как если бы оно содержало спецификатор extern.
extern "C" int x; // a declaration and not a definition
// The above line is equivalent to extern "C" { extern int x; }
extern "C" { int x; } // a declaration and definition
extern "C" double f();
static double f(); // error: linkage conflict
extern "C" static void g(); // error: linkage conflict
extern"C" позволяет включать в программу на C++ заголовочные файлы, содержащие объявления функций из библиотеки C, но если тот же заголовочный файл используется в программе на C, то extern"C" (который недопустим в C) необходимо скрыть с помощью соответствующей #ifdef, как правило, __cplusplus:
#ifdef __cplusplus extern "C" int foo(int, int); // C++ compiler sees this #else int foo(int, int); // C compiler sees this #endif
Из современных компиляторов только Oracle Studio различает типы функций с языковой связью "C" и "C++"; остальные не допускают перегрузки, отличающиеся только языковой связью, в том числе наборы перегрузок, требуемые стандартом C++ (std::qsort, std::bsearch, std::signal, std::atexit и std::at_quick_exit): ошибка GCC 2316, ошибка Clang 6277, вопрос CWG 1555.
extern "C" using c_predfun = int(const void*, const void*);
extern "C++" using cpp_predfun = int(const void*, const void*);
// ill-formed, but accepted by most compilers
static_assert(std::is_same<c_predfun, cpp_predfun>::value,
"C and C++ language linkages shall not differentiate function types.");
// following declarations do not declare overloads in most compilers
// because c_predfun and cpp_predfun are considered to be the same type
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]
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/language_linkage