clang 错误:非类型模板参数是指没有链接的函数 -- bug

clang error: non-type template argument refers to function that does not have linkage -- bug?

本文关键字:链接 函数 bug 错误 类型 参数 clang      更新时间:2023-10-16

我有一些非常简单(C++11(的代码,最新的clang(3.4版主干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 可以很好地编译它,证明问题出在类型是函数本地的。

那么 clang 发出此错误是否正确,还是标准不支持此错误(在这种情况下,GCC 允许它过于宽松(?


编辑#1 :需要明确的是,这不是这个问题的重复,因为"不能使用本地类型作为模板参数">限制已在 C++11 中删除。但是,目前尚不清楚使用本地类型是否涉及链接影响,以及 clang 在发出此错误时是否正确。


编辑#2 :已确定 clang 为上述代码发出错误是正确的(请参阅 @jxh 的答案(,但它也错误地发出以下代码的错误(using声明从foo<Bar>()范围移动到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;
}

根据 C++.11 §3.5 程序和链接 ¶2 中没有链接的定义,我最初认为foo<Bar>没有链接,因为它不能被任何其他范围通过名称引用,除了定义类型Bar(即main()(。但是,这是不正确的。这是因为具有外部链接的名称的定义被描述为:

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

对于模板函数,情况始终如此。这是因为还有另一个范围可以引用名称。也就是说,模板函数可以引用自身。因此,foo<Bar>具有外部联系。zneak的答案EDIT 2与clang开发人员有一封电子邮件,确认foo<Bar>应该有外部链接。

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

类型、非模板模板参数的模板参数应为以下参数之一:...

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

    我来晚了,但标准说类型局部函数没有链接(§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所指出的,这些名称也是在同一个翻译单元中定义的,所以我不确定该怎么看待这个。


    编辑2:clang的人确认这是一个错误。