Почему шаблоны не могут находиться в блоках extern "C"?

это последующий вопрос к ответ to можно ли ввести указатель на extern - "C" -тип функции в шаблоне?

этот код не компилируется с g++, Visual C / C++ и Comeau C / C++ С в основном тем же сообщением об ошибке:

#include <cstdlib>

extern "C" {
    static int do_stuff(int) {
        return 3;
    }

    template <typename return_t_, typename arg1_t_>
    struct test {
        static void foo(return_t_ (*)(arg1_t_)) { }
    };
}

int main()
{
    test<int, int>::foo(&do_stuff);
    return EXIT_SUCCESS;
}

g++ говорит "error: template with C linkage", Visual C/C++ выдает ошибку компилятора C2894, и Comeau C / C++ говорит " ошибка: это объявление может не имеют extern "C" связь".

дело в том, что все довольны:

#include <cstdlib>

extern "C" {
    static int do_stuff(int) {
        return 3;
    }

    struct test {
        static void foo(int (*)(int)) { }
    };
}

int main()
{
    test::foo(&do_stuff);
    return EXIT_SUCCESS;
}

раздел 7.5, спецификации связей, стандартных состояний C++:

связь языка C игнорируется для имен членов класса и функции-члена тип функций-членов класса.

и это даже приводит пример:

extern "C" {
    class X {
        void mf(); // the name of the function mf and the member
                // function's type have C++ language linkage
        void mf2(void(*)()); // the name of the function mf2 has C++ language
                // linkage; the parameter has type pointer to C function
    };
}

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

Почему же тогда Глава 14, Шаблоны, стандартного состояния C++98:

имя шаблона может иметь связи (3.5). Шаблон, явная специализация шаблона (14.7.3) и частичная специализация шаблона класса не должны иметь связи C.

что означает, что шаблон "может" иметь связь? Что такое связь шаблонов?

Почему явно запрещено иметь шаблон с связью C, когда класс в порядке, и все функции-члены экземпляров шаблона (конструктор по умолчанию, деструктор и перегрузка оператора присваивания) будут иметь связь c++?

5 ответов


что означает, что шаблон "может" иметь связь? Что такое связь шаблонов?

все имена либо имеют внешнюю связь, внутреннюю связь, либо не имеют связи (c++03 §3.5p2), но это не та же связь, что и языковая связь. (Я знаю, это сбивает с толку. C++0x также значительно меняет ситуацию с привязкой.) Внешняя связь требуется для всего, что используется в качестве аргумента шаблона:

void f() {
  struct S {};
  vector<S> v;  // Not allowed as S has internal linkage.
}

обратите внимание, что C++98 имеет "может" в том, что вы цитируется §14p4, но C++03 удаляет "может", поскольку шаблоны не могут быть объявлены в контексте, который дал бы им внутреннюю связь:

void f() {
  // Not allowed:
  template<class T>
  struct S {};
}

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


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


, потому что extern C отключает искажение имен, которые используют шаблоны

чтобы увидеть, что шаблоны реализованы с искажением имени, компиляцией и декомпиляцией:

#include <cassert>

template <class C>
C f(C i) { return i; }

int main() {
    f<int>(1);
    f<double>(1.5);
}

С:

g++ -c -g -std=c++98 main.cpp
objdump -Sr main.o

вывод содержит:

int main() {
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   48 83 ec 10             sub    x10,%rsp
    f<int>(1);
   8:   bf 01 00 00 00          mov    x1,%edi
   d:   e8 00 00 00 00          callq  12 <main+0x12>
            e: R_X86_64_PC32    _Z1fIiET_S0_-0x4
    f<double>(1.5);
  12:   48 b8 00 00 00 00 00    movabs x3ff8000000000000,%rax
  19:   00 f8 3f 
  1c:   48 89 45 f8             mov    %rax,-0x8(%rbp)
  20:   f2 0f 10 45 f8          movsd  -0x8(%rbp),%xmm0
  25:   e8 00 00 00 00          callq  2a <main+0x2a>
            26: R_X86_64_PC32   _Z1fIdET_S0_-0x4
}
  2a:   b8 00 00 00 00          mov    x0,%eax
  2f:   c9                      leaveq 
  30:   c3                      retq

обратите внимание, как все callq были сделаны, чтобы называть странные имена, как _Z1fIiET_S0_.

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

Смотрите также: в источнике C++, каков эффект extern "C"?


потому что в C.