27

这是对Is it possible to typedef a pointer-to-extern-“C”-function type 在模板中的答案的后续问题吗?

此代码无法使用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++ 说“错误:带有 C 链接的模板”,Visual C/C++ 发出编译器错误C2894,Comeau C/C++ 说“错误:此声明可能没有外部“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;
}

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
    };
}

如果外部“C”块中允许模板,则实例化的成员函数将具有 C++ 链接。

那么,为什么 C++98 标准的第 14 章,模板指出:

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

模板“可能”具有链接是什么意思?什么是模板链接?

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

4

5 回答 5

18

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

于 2011-02-02T17:34:39.023 回答
12

模板“可能”具有链接是什么意思?什么是模板链接?

所有名称要么具有外部链接、内部链接,要么没有链接(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 {};
}
于 2011-02-02T18:49:31.950 回答
6

因为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    $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

请注意,所有人callq都是如何称呼奇怪的名字的,例如_Z1fIiET_S0_.

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

I have written a more detailed answer at: What is the effect of extern "C" in C++?

于 2015-07-01T12:46:22.873 回答
3

因为模板函数名称需要用附加信息修饰,并extern "C"关闭修饰。的目的extern "C"是能够声明可以使用 C 链接调用的函数,这显然无法与模板函数一起使用。

于 2011-02-02T17:34:34.700 回答
-4

因为 C 中没有模板。

于 2011-02-02T17:33:49.763 回答