为什么模板不能在extern"C"块中?

Dan*_*ien 26 c++ templates extern linkage

这是一个后续问题的答案,以是否有可能的typedef指针到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;
}
Run Code Online (Sandbox Code Playgroud)

g ++说"错误:带C链接的模板",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;
}
Run Code Online (Sandbox Code Playgroud)

C++标准的第7.5节"链接规范"规定:

对于类成员的名称和类成员函数的成员函数类型,将忽略AC语言链接.

它甚至给出了一个例子:

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
    };
}
Run Code Online (Sandbox Code Playgroud)

如果在extern"C"块中允许模板,那么实例化的成员函数将具有C++链接.

那么,为什么C++ 98 Standard的第14章模板状态如下:

模板名称可能具有链接(3.5).模板,模板显式特化(14.7.3)和类模板部分特化不应具有C链接.

模板"可能"具有联系意味着什么?什么是模板链接?

为什么它明确禁止具有与C链接的模板,当一个类是好的,和模板(默认构造函数,析构函数和赋值运算符重载)的实例的所有成员函数将有C++联动?

Mar*_*som 14

模板不是实际代码,它们只是编译器的指南,用于在知道模板参数后如何生成代码.因此,在您尝试使用它们之前,它们实际上并不存在.您无法提供与不存在的内容的链接.

  • @Daniel,课程具体而且毫不含糊.虽然这些方法可能无法从C中访问,但数据成员可能仍然有用. (2认同)
  • 数据成员不会受损,也没有调用约定.该标准明确指出extern"C"不适用于它们.我不知道如何在这里使用它们. (2认同)
  • 所有类成员都忽略C语言链接,而不仅仅是成员函数.标准实际上给出了两个例子,我引用了第二个例子.第一个显示静态和成员变量,并声明两者都具有C++语言链接. (2认同)

Fre*_*urk 11

模板"可能"具有联系意味着什么?什么是模板链接?

所有名称都有外部链接,内部链接或没有链接(C++03§3.5p2),但这与语言链接不同.(令人困惑,我知道.C++ 0x也会通过链接大大改变一切.)用作模板参数的任何东西都需要外部链接:

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

请注意,C++ 98有"可能"在你所引述§14p4的,但C++ 03中删除"可能",因为模板不能将给他们内在联系上下文声明:

void f() {
  // Not allowed:
  template<class T>
  struct S {};
}
Run Code Online (Sandbox Code Playgroud)


Cir*_*四事件 6

因为extern C禁用名称修改,哪些模板使用

要查看模板是通过名称修改实现的,请编译和反编译:

#include <cassert>

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

int main() {
    f<int>(1);
    f<double>(1.5);
}
Run Code Online (Sandbox Code Playgroud)

和:

g++ -c -g -std=c++98 main.cpp
objdump -Sr main.o
Run Code Online (Sandbox Code Playgroud)

输出包含:

int main() {
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   48 83 ec 10             sub    $0x10,%rsp
    f<int>(1);
   8:   bf 01 00 00 00          mov    $0x1,%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 $0x3ff8000000000000,%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    $0x0,%eax
  2f:   c9                      leaveq 
  30:   c3                      retq
Run Code Online (Sandbox Code Playgroud)

请注意如何将所有callq名称都称为_Z1fIiET_S0_.

其他依赖于名称修改的特性也是如此,例如函数重载。

我写了一个更详细的答案:在 C++ 中 extern "C" 的作用是什么?