【问题标题】:Why can't templates be within extern "C" blocks?为什么模板不能在外部“C”块内?
【发布时间】:2011-02-02 17:30:55
【问题描述】:

这是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++ 说“错误:此声明可能没有外部“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
    };
}

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

那么,为什么 C++98 标准的第 14 章“模板”状态:

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

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

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

【问题讨论】:

  • 啊,你找到了禁止模板有C链接的条款!

标签: c++ templates extern linkage


【解决方案1】:

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

【讨论】:

  • 为什么 C++ 标准明确允许在 extern "C" 块中声明类?
  • @Daniel,类是具体且明确的。尽管这些方法可能无法从 C 中访问,但数据成员可能仍然有用。
  • 数据成员没有被破坏,也没有调用约定。该标准明确表示 extern "C" 不适用于它们。我看不出从 C 语言中使用它们有什么意义。
  • 所有类成员都忽略 C 语言链接,而不仅仅是成员函数。该标准实际上为此提供了两个示例,我引用了第二个示例。第一个显示了一个静态变量和一个成员变量,并声明两者都有 C++ 语言链接。
【解决方案2】:

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

所有名称要么具有外部链接、内部链接,要么没有链接(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 {};
}

【讨论】:

    【解决方案3】:

    因为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++?写了一个更详细的答案

    【讨论】:

      【解决方案4】:

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

      【讨论】:

      • 标准中引用的示例说可以在外部“C”块中声明一个类,并且它的成员函数使用 C++ 链接声明。如果允许使用模板,则其成员函数也将具有 C++ 链接。这就是我认为您所指的“附加信息”。我想我不明白有什么区别。
      • @Daniel,类名不需要进行装饰,但模板名需要,否则编译器将如何区分template&lt;char&gt;template&lt;wchar_t&gt;
      【解决方案5】:

      因为 C 中没有模板。

      【讨论】:

      • C 中也没有类,但允许类声明(和定义)出现在 extern "C" 块中。为什么?
      • 没有勺子。
      猜你喜欢
      • 2018-10-04
      • 1970-01-01
      • 2014-06-18
      • 2021-12-27
      • 2012-10-02
      • 2020-11-22
      • 2020-12-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多