Иногда я стараюсь следовать логике некоторых правил, иногда логика того, почему все происходит так, как они делают, побеждает любой закон, о котором я знаю.
Типично шаблон, который он описывает как нечто, что живет только на этапе компиляции, и это точно эквивалентно написанию вручную некоторой функции foo
для любого заданного типа T
Итак, почему этот код не компилируется (я использую С++ 11 с gcc
и clang
в данный момент, но я не думаю, что это уместно в этом случае)?
#include <iostream>
#include <cstdint>
#include <cstdlib>
extern "C" {
template <typename T>
T foo(T t)
{
return t;
}
}
int main()
{
uint32_t a = 42;
std::cout << foo(a) << '\n';
return EXIT_SUCCESS;
}
И то, что побеждает всю логику, заключается в том, что жалоба связана с связью, а неявное сообщение состоит в том, что этот код не генерирует функцию, он генерирует что-то еще, что после компиляции оно не подходит для связи стиля C.
Какова техническая причина, по которой этот код не компилируется?
Давайте посмотрим на это с простой точки зрения. По крайней мере, использование extern "C"
приведет к удалению имени C++. Итак, у нас есть ваш шаблон, и мы будем создавать его дважды.
int foo(int val);
float foo(float val);
В соответствии с правилами именования C они должны иметь одно и то же имя foo
с точки зрения компоновщика. Если они имеют одно и то же имя, мы не можем различать их, и у нас будет ошибка.
В C++ правила для того, как имена искажаются, определяется реализацией. Поэтому компиляторы C++ будут применять имя для этих двух функций для их дифференциации. Возможно, мы назовем их foo_int
и foo_float
.
Поскольку C++ может это сделать, у нас нет проблем. Но extern "C"
требует, чтобы компилятор применял правила именования C.
"связь" - это немного вводящий в заблуждение термин. Главное, что extern "C"
меняют название. То есть он создает имена символов в объектных файлах, которые совместимы с типом символов, которые создавал бы эквивалентный код C. Таким образом, он может связываться с объектным кодом C.... но это нечто иное, чем указание static
или extern
связи.
Но шаблоны не имеют эквивалента C, а имя mangling используется, чтобы удостовериться, что разные экземпляры данной шаблонной функции приводят к разным именам символов (так, чтобы компоновщик знал, какой из них использовать в данном месте).
Таким образом, нет способа предоставить шаблоны C linkage; вы просите компилятор сделать две принципиально несовместимые вещи.
extern
который уже полностью связан со связыванием, и на самом деле он не помогает вам найти конкретную ошибку в определенной строке или семантике.
Ну, другие ответы объяснили, почему он не работает со стороны C++.
С стороны C есть рабочие раунды, но они не переносимы.
Вы можете просто не использовать ключевое слово extern "C"
, создавать функции, зависящие от имени, а затем в ссылке на C-код к фактическим искаженным именам.
Чтобы это было проще, вы могли бы также использовать функцию GCC abi::__cxa_demangle()
сочетании со abi::__cxa_demangle()
таблицей, чтобы вам не нужно было знать, что такое имена искалеченных функций (только их демаршированная подпись).
Но все это на самом деле.
Конечно, если вы вызываете только функции шаблона из кода C, они никогда не будут созданы для начала. Поэтому вам нужно убедиться, что они вызываются в коде C++, чтобы убедиться, что они присутствуют в объектном файле.
extern "C"
влияет на фазу линковки, не совсем то, что "работает или нет", это то, что стандарт говорит оextern
, также код внутриextern
который все еще компилируется с Компилятор C ++.