Шаблон External Linkage? Может кто-нибудь объяснить это? - PullRequest
5 голосов
/ 27 июля 2010

Имя шаблона имеет связь (3.5).Шаблон функции, не являющейся членом, может иметь внутреннюю связь;любое другое имя шаблона должно иметь внешнюю связь.Сущности, сгенерированные из шаблона с внутренней связью, отличаются от всех сущностей, сгенерированных в других единицах перевода.

Я знаю о внешней связи с использованием ключевого слова

extern "C"

EX:

extern "C" {   template<class T>  class X { };   }

но у них шаблон не должен иметь связи C

что на самом деле означало приведенное выше утверждение?

Может ли кто-нибудь это объяснить?

Ответы [ 4 ]

24 голосов
/ 09 августа 2010

extern "C" объявляет что-то, имеющее связь с языком C . Это отличается от внешней связи и внутренней связи . По умолчанию все в программе на C ++ имеет связь на языке C ++, хотя вы можете повторить это, указав extern "C++".

external linkage означает, что имя является видимым для других исходных файлов, скомпилированных отдельно, при условии, что вы включили правильные заголовки или предоставили правильные объявления. Это то, что позволяет вам определять функцию foo в a.cpp и вызывать ее из b.cpp. Большинство имен в области имен в программе на C ++ имеют внешнюю связь. Исключения составляют те, которые имеют внутреннюю связь , и те, которые имеют отсутствие связи . Вы можете явно пометить что-то как имеющее внешнюю связь, указав extern. Это отличается от extern "C".

внутренняя связь означает, что имя является уникальным для текущей единицы компиляции, и вы не можете получить доступ к переменной или функции из другого исходного файла. Переменные области видимости файлов и функции, объявленные static, имеют внутреннюю связь. Кроме того, const целочисленные переменные в области имен, которые инициализируются константным выражением, по умолчанию имеют внутреннюю связь, хотя вы можете переопределить ее явным extern.

Наконец, локальные переменные и классы имеют без связи . Имена являются локальными для функции, в которой они объявлены, и не могут быть доступны извне этой функции. Вы можете использовать extern, чтобы указать, что вы действительно хотите получить доступ к переменной в области имен.

Шаблоны не могут быть определены в локальной области, но могут иметь внутреннюю или внешнюю связь.

int i; // namespace scope variable has external linkage
extern int j; // explicitly mark j with external linkage
static int k; // k has internal linkage
int const n=42; // internal linkage
extern int const m=99; // external linkage

void foo(); // foo has external linkage; it may be defined in this source file or another
extern void foo(); // explicitly mark foo with external linkage
static void bar(); // bar has internal linkage, and must be defined in this source file

void foo(){} // definition of foo, visible from other source files
void bar(){} // definition of bar, not visible from other source files (internal linkage)

static void baz(){} // declare and define baz with internal linkage

template<typename T> void foobar(){} // foobar has external linkage
template<typename T>
static void foobaz(){} // foobaz has internal linkage

void wibble()
{
    int i; // local, no linkage
    extern int i; // references i, declared above with external linkage
}

extern "C"
{
    int i2; // namespace scope variable has external linkage, and "C" linkage
    extern int j2; // explicitly mark j2 with external linkage and "C" linkage
    static int k2; // k2 has internal linkage and "C" linkage
    int const n2=42; // internal linkage and "C" linkage
    extern int const m2=99; // external linkage and "C" linkage

    void foo2(); // foo2 has external linkage and "C" linkage
    static void bar2(); // bar2 has internal linkage and "C" linkage

    void foo2(){} // definition of foo2, still with external linkage and "C" linkage
    void bar2(){} // definition of bar2, still with internal linkage and "C" linkage

    static void baz(){} // declare and define baz with internal linkage
}

Сообщение об ошибке правильное - шаблоны не могут иметь extern "C" связь.

На базовом уровне шаблоны не могут иметь связи extern "C", потому что они несовместимы с C. В частности, шаблон не просто определяет отдельный класс или функцию, но семейство классов или функций, которые совместно используют один и тот же имя, но различаются по параметрам шаблона.

Только одна функция с заданным именем может быть объявлена ​​extern "C". Это имеет смысл, когда вы думаете об искажении имени - в C функция foo обычно вызывается либо foo, либо _foo в таблице символов. В C ++ может быть много перегрузок foo, поэтому подпись включена в «искаженное» имя в таблице символов, и вы можете получить $3fooV или foo$void или что-то еще, чтобы отличить foo(void) от foo(int) и так далее. В C ++ одиночная перегрузка, помеченная extern "C", искажается в соответствии со схемой C для данной платформы, тогда как другие перегрузки сохраняют свое обычное искаженное имя.

Объявление шаблона extern "C" потребует, чтобы все экземпляры были extern "C", что противоречит правилу extern "C" только для одной функции с данным именем.

Хотя C не имеет искажения имени в течение struct s, может быть только один struct с данным именем. Таким образом, запрет на extern "C" для шаблонов классов также имеет смысл - шаблон определяет семейство классов с одинаковым именем, которое соответствует C struct?

8 голосов
/ 27 июля 2010

Внимательно прочитав цитату, которую вы написали, вы заметите, что кроме шаблонов функций, не являющихся членами, которые могут иметь внутреннюю связь, все остальные шаблоны имеют внешнюю связь.Нет необходимости добавлять ключевые слова, и ключевые слова не могут быть добавлены туда.

Описание того, что означает связь, содержится в §3.5 / 2, в частности внешняя связь определяется как:

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

КомуДля принудительной внутренней привязки функции, не являющейся членом шаблона, вы можете использовать ключевое слово static, но вы не можете сделать то же самое с другими шаблонами:

template <typename T>
static void foo( T ) {}

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

внутренняя связь: §3.5 / 2

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

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

namespace {
   template <typename T>
   class test {};
}

Хотя пространство имен без имени не делает связь внутренней, она гарантирует, что будетнет конфликта имен, так как оно будет в уникальном пространстве имен.Эта уникальность гарантирует, что код не доступен из других модулей перевода.Безымянные пространства имен считаются лучшей альтернативой ключевому слову static §7.3.1.1 / 2

Использование статического ключевого слова не рекомендуется при объявлении объектов в области пространства имен (см. Приложение D).;Пространство имен без имен предоставляет превосходную альтернативу

С другой стороны, когда вы говорите, что вы:

знаете о внешней связи, используя ключевое слово extern "C"

Вы не.extern "C" не является запросом на внешнюю связь.Перечитайте спецификации.extern "C" является спецификацией связывания и указывает компилятору использовать связь в стиле "C" внутри блока для взаимодействия с кодом C или библиотеками, которые уже работают таким образом, например, dlopen и семейством.Это описано в §7.5

2 голосов
/ 27 июля 2010

extern "C" используется для изменения имени символа функции C ++, чтобы использовать их из программы на C.

В C ++ прототип функции «закодирован» в имени символа, это требование для перегрузки. Но в Си такой функции нет.

extern "C" позволяет вызывать функцию C ++ из программы на C.

extern "C" - это не то, что вы ищете.

Не могли бы вы объяснить, что вы хотите сделать?

1 голос
/ 08 августа 2010

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

Последовательность extern "X" позволяет изменить языковую связь следующей функции или блока на язык X. Это не означает внешнюю связь, поэтому ваша исходная предпосылка:

Я знаю о внешней связи, используя ключевое слово extern "C"

равно ложно . Вы не знаете, что это значит. Обратитесь к 7.5 в стандарте. Языковая связь влияет на то, как компилятор обрабатывает параметры, и применяет ли он (и, возможно, каким образом) искажение имени к символам.

Помимо вашей настойчивости в этой конкретной ошибке, компилятор жалуется на ваш код, потому что он является недействительным в соответствии со стандартом. В частности §14 [temp] / 4:

Имя шаблона имеет связь (3.5). Шаблон функции, не являющейся членом, может иметь внутреннюю связь; любое другое имя шаблона должно иметь внешнюю связь. Объекты, созданные из шаблона с внутренней связью, отличаются от всех объектов, созданных в других единицах перевода. Шаблон, явная специализация шаблона (14.7.3) или частичная специализация шаблона класса не должны иметь связи C . Если связь одного из них отличается от C или C ++, поведение определяется реализацией. Определения шаблонов должны соответствовать одному правилу определения (3.2). [Примечание: аргументы по умолчанию для шаблонов функций и для функций-членов шаблонов классов считаются определениями с целью создания экземпляра шаблона (14.5) и должны также подчиняться одному правилу определения.]

Я действительно считаю, что прежде чем пытаться оценить, насколько разные компиляторы соответствуют стандарту, вам следует потратить время на понимание стандарта. Иметь вопросы вполне нормально, и есть люди, которые стараются на них ответить. Показ того, что вы прочитали ответы и попытались понять, что они означают, - это просто знак уважения. Какая часть последнего абзаца в предыдущем ответе здесь неясна? Вы читали это? Вы поняли, это? Если нет, то почему вы не спросили в комментарии к ответу?

...