【问题标题】:clang error: non-type template argument refers to function that does not have linkage -- bug?clang 错误:非类型模板参数指的是没有链接的函数——错误?
【发布时间】:2013-09-01 04:18:09
【问题描述】:

我有一些非常简单的 (C++11) 代码,最新的 clang (version 3.4 trunk 187493) 无法编译,但 GCC 编译得很好。

代码(如下)使用函数本地类型 Bar 实例化函数模板foo,然后尝试将其地址用作类的非类型模板参数-模板Func:

template<void(*FUNC_PTR)(void)>
struct Func {};

template<typename T> extern inline
void foo() {
    using Foo = Func<foo<T>>;
}
int main() {
    struct Bar {}; // function-local type
    foo<Bar>();
    return 0;
}

clang 发出以下错误:

错误:非类型模板参数指的是函数 'foo' 没有链接

但是,如果我将类型 Bar 移动到全局范围(通过将其从函数中取出),那么 clang 可以很好地编译它,证明问题在于类型为 function-local

那么发出这个错误的 clang 是正确的,还是标准不支持这个(在这种情况下 GCC 允许它太宽松了)?


编辑 #1: 需要明确的是,这不是this question 的重复,因为 “不能使用本地类型作为模板参数” 限制在 C++11 中被删除。 然而,目前还不清楚使用本地类型是否存在链接影响,以及发出此错误时 clang 是否正确。


EDIT #2 : 已确定 clang 发出上述代码的错误是正确的(参见 @jxh 的回答),但它不正确也会发出错误对于以下代码(using 声明从 foo&lt;Bar&gt;() 范围移动到 main() 范围):
template<void(*FUNC_PTR)(void)>
struct Func {};

template<typename T> extern inline
void foo() {}

int main() {
    struct Bar {};
    using F = Func<foo<Bar>>;
    return 0;
}

【问题讨论】:

  • 最新版本的 Xcode 附带的 clang 表示“错误:非模板参数指的是具有内部链接的函数 'foo'”。跨度>
  • @jxh,即使将 --std=c++11 传递给 clang,也会发生这种情况。
  • @jxh:这不是那个问题的重复,因为 C++11 删除了该限制,并且 clang 声称这是某种链接错误。
  • @etherice: 没关系inline - 我不知道是否可以使用没有链接的类型实例化foo 模板并获得具有链接的东西...
  • 如果 14/4 能谈谈模板特化的链接,那就太好了。它似乎在回避这个问题。

标签: c++ templates gcc c++11 clang


【解决方案1】:

根据 C++.11 §3.5 Program and links ¶2 中 no links 的定义,我最初认为 foo&lt;Bar&gt; 没有链接,因为它不能被除了定义类型Bar(即main())之外的任何其他范围的名称。但是,这是不正确的。这是因为带有外部链接的名称的定义被描述为:

当一个名称具有外部链接时,它所表示的实体可以被其他翻译单元的范围或同一翻译单元的其他范围的名称引用。

对于模板函数,情况总是如此。这是因为还有另一个范围可以引用该名称。即,模板函数可以引用自身。因此,foo&lt;Bar&gt; 具有外部链接。 zneak's answer, EDIT 2,与 clang 开发人员的电子邮件线程确认 foo&lt;Bar&gt; 应该有外部链接。

因此,来自 C++.11 §14.3.2 模板非类型参数 ¶1:

template-argument 用于非类型、非模板 template-parameter 应为以下之一: ...

  • 一个常量表达式 (5.19),它指定具有静态存储持续时间和外部或内部链接的对象或具有外部或内部链接的函数的地址,包括函数模板和函数 template-ids但不包括非静态类成员,表示(忽略括号)为&amp;id-expression,但如果名称引用函数或数组,则可以省略&amp;,并且应省略如果对应的 template-parameter 是一个引用; ...
  • 最相关的项目符号是第三个项目符号。由于foo&lt;bar&gt; 具有外部链接,因此将其作为非类型模板参数 传递应该没问题。

    【讨论】:

    • @zneak:你不能从main调用foo&lt;Bar&gt;(),但是你可以main实例化Func&lt;foo&lt;T&gt;&gt;,证明jxh提供了正确的答案。
    • 那么,如果你有全局void (*p)();p = foo&lt;Bar&gt;; 是否可以从main 工作?
    • @jxh: 是的foo&lt;Bar&gt;(单独)总是从main 工作,但尝试将其用作非类型模板参数,例如Foo&lt;foo&lt;Bar&gt;&gt;(甚至Foo&lt;&amp;foo&lt;Bar&gt;&gt;)它确实工作。看来clang太严格了,gcc太宽松了……在这种情况下都没有正确实现。
    • 是的,foo&lt;Bar&gt; 可以分配给全局p
    • 好吧,根据@zneak从clang开发者那里得到的回复,我认为有一个漏洞可以让foo&lt;Bar&gt;被认为有外部链接。
    【解决方案2】:

    我迟到了,但标准规定类型本地函数没有链接(第 3.5:8 节):

    这些规则未涵盖的名称没有链接。此外,除非另有说明,在块范围 (3.3.3) 中声明的名称没有链接

    同一节继续说:

    没有链接的类型不应用作具有外部链接的变量或函数的类型,除非

    • 实体具有 C 语言链接 (7.5),或
    • 实体在未命名的命名空间 (7.3.1) 中声明,或
    • 实体未使用 odr (3.2) 或在同一翻译单元中定义。

    事实上,Clang 允许这样做:

    namespace
    {
        template<void (*FUNC_PTR)(void)>
        struct Func {};
    
        template<typename T>
        void foo() {}
    }
    
    int main() {
        struct Bar {}; // function-local type
        Func<foo<Bar>> x;
    }
    

    并且会在没有匿名命名空间的情况下拒绝它。


    编辑 1:正如 jxh 所指出的,名称也是在同一个翻译单元中定义的,所以我不确定如何看待这个。


    编辑 2The guys at clang confirm it's a bug.

    【讨论】:

    • 第三个项目符号不适用吗,因为它们在同一个翻译单元中?
    • 当我第一次写我的答案时,我最初也提到了这一部分,但我删除了它,因为我认为我不能真正将函数名视为一种类型。
    • @zneak: foo&lt;Bar&gt; 已经从 main 工作,即使 foo&lt;&gt; 没有在匿名命名空间中声明。与之前的唯一区别是类模板Func 可以从main 内部用foo&lt;Bar&gt; 实例化,但这似乎与您所说的段落无关,因为“变量的类型”FUNC_PTRvoid(*)(void)它并没有真正使用foo&lt;Bar&gt; 作为FUNC_ADDR 的“类型”,而是它的地址/指针值。但我不能责怪 clang(或 gcc),因为有很多解释和混淆的空间。
    • 好吧,我完全糊涂了。电子邮件交换说foo&lt;Bar&gt; 有外部链接。但是,除了main() 本身的代码之外,任何代码都无法调用它。哦等等,外部链接的定义提供了一个漏洞......
    • @jxh:clang 开发团队的回应实际上是有道理的,这也是我最初的怀疑。函数foo&lt;main::Bar&gt; 具有外部链接,但编译器错误地降级了对象的链接,因为它是用没有链接的函数本地类型实例化的,因此不允许它用于需要“地址”的非类型参数[...] 外部或内部链接的对象”。所以这并不是真正的标准漏洞什么的,它只是clang中的“联动降级”错误。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-10
    • 1970-01-01
    • 2021-02-07
    相关资源
    最近更新 更多