Spec-Zone.ru › C++

Языковая связь

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

Это также можно использовать, чтобы отсоединить объявление от его модуля. См. Принадлежность модулю.

(начиная с 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 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);

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

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]

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/language_linkage

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API