Namespaces
Variants

Language linkage

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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

Это также может использоваться для отделения объявления от его модуля. См. Владение модулем .

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

Содержание

Объяснение

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

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

  1. "C++" , языковая связь по умолчанию.
  2. "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);

Ключевые слова

extern

Отчеты о дефектах

Следующие отчеты об изменениях в поведении, являющиеся дефектными, были применены ретроактивно к ранее опубликованным стандартам 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]