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

问题描述 投票:25回答:5

这是an answerIs it possible to typedef a pointer-to-extern-“C”-function type within a template?的后续问题

此代码无法使用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 ++说“错误:此声明可能没有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;
}

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

对于类成员的名称和类成员函数的成员函数类型,将忽略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 ++链接。

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

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

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

为什么明确禁止使用带有C链接的模板,当一个类没问题时,模板实例化的所有成员函数(默认构造函数,析构函数和赋值运算符重载)都会有C ++链接?

c++ templates extern linkage
5个回答
10
投票

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

所有名称都有外部链接,内部链接或没有链接(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 {};
}

14
投票

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


4
投票

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

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

我在What is the effect of extern "C" in C++?写了一个更详细的答案


3
投票

因为模板函数名称需要用附加信息进行修饰,而extern "C"会关闭装饰。 extern "C"的目的是能够声明可以使用C链接调用的函数,这显然不会对模板函数起作用。


-4
投票

因为C中没有模板。

© www.soinside.com 2019 - 2024. All rights reserved.