Пространства имён
Варианты
Действия

Языковое связывание

Материал из cppreference.com
< cpp‎ | language
 
 
Язык С++
Общие темы
Управление потоком
Операторы условного выполнения
Операторы итерации (циклы)
Операторы перехода
Функции
Объявление функции
Выражение лямбда-функции
Спецификатор inline
Спецификации динамических исключений(до C++17*)
Спецификатор noexcept(C++11)
Исключения
Пространства имён
Типы
Спецификаторы
decltype(C++11)
auto(C++11)
alignas(C++11)
Спецификаторы длительности хранения
Инициализация
Выражения
Альтернативные представления
Литералы
Логические - Целочисленные - С плавающей запятой
Символьные - Строковые - nullptr(C++11)
Определяемые пользователем(C++11)
Утилиты
Атрибуты(C++11)
Types
Объявление typedef
Объявление псевдонима типа(C++11)
Casts
Неявные преобразования - Явные преобразования
static_cast - dynamic_cast
const_cast - reinterpret_cast
Выделение памяти
Классы
Свойства функции класса
Специальные функции-элементы
Шаблоны
Разное
 
Обьявления
 

Обеспечивает связывание между программными блоками, написанными на разных языках программирования.

externстроковый-литерал{последовательность-объявлений (необязательно)} (1)
externстроковый-литералобъявление (2)
1) Применяет строковый-литерал спецификации языка ко всем типам функций, именам функций с внешним связыванием и переменным с внешним связыванием, объявленным в последовательности-объявлений.
2) Применяет строковый-литерал спецификации языка к одному объявлению или определению.
строковый-литералнеоценённый строковый литерал, который именует требуемое языковое связывание
последовательность-объявлений последовательность объявлений, которая может включать вложенные спецификации связывания
объявление объявления

Содержание

[править]Объяснение

Каждый тип функции, каждое имя функции с внешним связыванием и каждое имя переменной с внешним связыванием имеют свойство, называемое языковое связывание. Языковое связывание инкапсулирует набор требований, необходимых для компоновки программного блока, написанного на другом языке программирования: соглашение о вызовах, алгоритмы изменения имени и т.д.

Гарантируется поддержка только двух языковых связываний:

  1. "C++", языковое связывание по умолчанию.
  2. "C", которое позволяет связывать функции, написанные на языке программирования C, и определять в программе на "C++" функции, которые можно вызывать из блоков, написанных на C.
extern"C"{int open(constchar*path_name, int flags);// объявление функции C}   int main(){int fd = open("test.txt", 0);// вызывает функцию C из программы на C++}   // Эта функция C++ может быть вызвана из кода на Cextern"C"void handler(int){std::cout<<"Вызван обратный вызов\n";// Это может использовать С++}

Поскольку языковое связывание является частью каждого типа функции, указатели на функции также поддерживают языковое связывание. Языковое связывание типов функций (которое представляет соглашение о вызовах) и языковое связывание имён функций (которое представляет изменение имён) не зависят друг от друга:

extern"C"void f1(void(*pf)());// объявляет функцию f1 со связыванием C,// которая возвращает void и принимает указатель// на функцию C, которая возвращает void и// не принимает параметров   extern"C"typedefvoid FUNC();// объявляет FUNC как тип функции C,// которая возвращает void и не принимает параметров   FUNC f2;// имя f2 имеет связывание C++, но его тип это функция Cextern"C" FUNC f3;// имя f3 имеет связывание C, а его тип это функция C void()void(*pf2)(FUNC*);// имя pf2 имеет связывание C++, а его тип// "указатель на функцию C++, которая возвращает void и принимает один// аргумент типа 'указатель на функцию C, которая возвращает void и не// принимает параметров'"   extern"C"{staticvoid f4();// имя функции f4 имеет внутреннее связывание (без языка),// но тип функции имеет языковое связывание C}

Две функции с одним и тем же именем и одним и тем же списком параметров в одном и том же пространстве имён не могут иметь два разных языковых сязывания (обратите внимание, однако, что связывание параметра может допускать такую перегрузку, как в случае std::qsort и std::bsearch). Точно так же две переменные в одном и том же пространстве имён не могут иметь два разных языковых связывания.

[править]Специальные правила для связывания "C"

Когда элементы класса, дружественные функции с конечным предложением-require,(начиная с C++23) или нестатические функции-элементы появляются в языковом блоке "C", связывание их типов остаётся "C++" (но типы параметров, если есть, остаются "C"):

extern"C"{class X {void mf();// функция mf и её тип имеют языковое связывание C++void mf2(void(*)());// функция mf2 имеет языковое связывание C++;// параметр имеет тип “указатель на функцию C”};}

Когда функция или переменная объявлены (в любом пространстве имён) с языковым связыванием "C", все объявления функций (в любом пространстве имён) и все объявления переменных в глобальной области видимости с одним и тем же неполным именем должны ссылаться на одну и ту же функцию или переменную.

int x;   namespace A {extern"C"int x();// ошибка: то же имя, что и переменная x глобального// пространства имён}
namespace A {extern"C"int f();}   namespace B {extern"C"int f();// A​::​f и B​::​f ссылаются на одну и ту же функцию f// со связыванием C}   int A::f(){return98;}// определение для этой функции
namespace A {extern"C"int g(){return1;}}   namespace B {extern"C"int g(){return1;}// ошибка: переопределение той же функции}
namespace A {extern"C"int h();}   extern"C"int h(){return97;}// определение функции h со связыванием C// A​::​h и ​::​h ссылаются на одну и ту же функцию

Примечание: специальное правило, которое исключает дружественную функцию с предложениями require, позволяет объявить дружественную функцию с тем же именем, что и функция глобальной области видимости "C", используя фиктивное предложение:

template<typename T>struct A {struct B;};   extern"C"{template<typename T>struct A<T>::B{friendvoid f(B*) requires true{}// языковое связывание C игнорируется};}   namespace Q {extern"C"void f();// сформировано верно}
(начиная с C++23)

[править]Примечание

Спецификации языка могут появляться только в области видимости пространства имён.

Скобки в спецификации языка не определяют область видимости.

Когда языковые спецификации вложены друг в друга, действует самая внутренняя спецификация.

Функция может быть повторно объявлена без спецификации связывания после того, как она была объявлена со спецификацией языка, второе объявление будет повторно использовать первое языковое связывание. Обратное неверно: если первое объявление не имеет языкового связывания, предполагается "C++", и повторное объявление с другим языком является ошибкой.

Объявление, непосредственно содержащееся в спецификации языкового связывания, обрабатывается так, как если бы оно содержало спецификатор extern с целью определения связывания объявленного имени и является ли оно определением.

extern"C"int x;// объявление, а не определение// Приведенная выше строка эквивалентна extern "C" { extern int x; }   extern"C"{int x;}// объявление и определение   extern"C"double f();staticdouble f();// ошибка: конфликт связывания   extern"C"staticvoid g();// ошибка: конфликт связывания

extern"C" позволяет включать заголовочные файлы, содержащие объявления библиотечных функций C, в программу на C++, но если тот же заголовочный файл используется совместно с программой на C, extern"C" (что не разрешено в C) должно быть скрыто соответствующим #ifdef, обычно __cplusplus:

#ifdef __cplusplusextern"C"int foo(int, int);// компилятор С++ видит это#elseint foo(int, int);// компилятор C видит это#endif

Единственным современным компилятором, который различает типы функций с языковым связыванием "C" и "C++", является Oracle Studio, другие не допускают перегрузок, которые отличаются только языковым связыванием, включая наборы перегрузок, требуемые стандартом 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(constvoid*, constvoid*);extern"C++"using cpp_predfun =int(constvoid*, constvoid*);   // неправильно сформировано, но принимается большинством компиляторов 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++:

Номер Применён Поведение в стандарте Корректное поведение
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: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]
close