Связывание языков
Обеспечивает связывание между программными модулями, написанными на разных языках программирования.
extern строковая_литераль { последовательность_объявлений (необязательно) } | (1) | |
extern строковая_литераль объявление | (2) |
| строковая_литераль | - | невычисляемая строковая литераль, которая называет требуемое связывание языков |
| последовательность_объявлений | - | последовательность объявлений, которая может включать вложенные спецификации связывания |
| объявление | - | объявление |
Объяснение
Каждый тип функции, каждое имя функции с внешней связью и каждое имя переменной с внешней связью имеет свойство, называемое связыванием языка. Связывание языка описывает набор требований, необходимых для связывания с программным модулем, написанным на другом языке программирования: конвенцией вызова, алгоритмом присвоения имен (разметкой имен) и т. д.
Гарантируется поддержка только двух связываний языков:
- "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
}Две функции с одинаковым именем и списком параметров в одном пространстве имён не могут иметь два разных связывания языков (заметим, однако, что связывание параметра может позволить такую перегрузку, как в случае std::qsort и std::bsearch). Аналогично, две переменные в одном пространстве имён не могут иметь два разных связывания языков.
Специальные правила для связывания "C"
Когда члены класса, дружественные функции с заключительной оставкой условий,(с 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”
};
}Когда функция или переменная объявляется (в любом пространстве имён) со связыванием языка "C", все объявления функций (в любом пространстве имён) и все объявления переменных в глобальной области видимости с тем же неопределённым именем должны ссылаться на одну и ту же функцию или переменную.
int x;
namespace A
{
extern "C" int x(); // error: same name as global-namespace variable x
}namespace A
{
extern "C" int f();
}
namespace B
{
extern "C" int f(); // A::f and B::f refer to the same function f with C linkage
}
int A::f() { return 98; } // definition for that functionnamespace A
{
extern "C" int g() { return 1; }
}
namespace B
{
extern "C" int g() { return 1; } // error: redefinition of the same function
}namespace A
{
extern "C" int h();
}
extern "C" int h() { return 97; } // definition for the C linkage function h
// A::h and ::h refer to the same function| Примечание: специальное правило, исключающее дружественные функции с заключительной оговоркой, позволяет объявить дружественную функцию с тем же именем, что и глобальная функция "C", используя фиктивную оговорку: 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++23) |
Примечания
Спецификации языка могут появляться только в области видимости пространства имён.
Фигурные скобки спецификации языка не создают области видимости.
Когда спецификации языка вложены, действует самая внутренняя спецификация.
Функция может быть повторно объявлена без спецификации связывания после объявления со спецификацией языка; второе объявление будет использовать первое связывание языка. Обратное неверно: если первое объявление не имеет связывания языка, оно предполагается "C++", а повторное объявление с другим языком является ошибкой.
Объявление, непосредственно содержащееся в спецификации связывания языка, рассматривается как содержащее 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 conflictextern "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
Единственный современный компилятор, который различает типы функций со связыванием языка "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*);
// 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 | дружественные функции с заключительной оговоркой и связыванием языка "C" имели конфликтующее поведение |
связывание языка "C" в этом случае игнорируется |
| CWG 2483 | C++98 | связывание типов статических членов функций появляются в блоках языка "C" было "C++" | связывание равно "C" |
Ссылки
- Стандарт C++23 (ISO/IEC 14882:2023):
- 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]
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/language_linkage