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

时间:2013-08-28 22:00:46

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

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

代码(下面)使用函数本地类型 foo实例化函数模板Bar,然后尝试将其地址用作非类型模板参数对于类模板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是否正确。

<小时/> 编辑#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;
}

2 个答案:

答案 0 :(得分:3)

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

  

当名称具有外部链接时,其表示的实体可以通过其他翻译单位的范围或同一翻译单位的其他范围中的名称来引用。

对于模板功能,情况总是如此。这是因为可以引用名称的另一个范围。即,模板功能可以参考其自身。因此,foo<Bar>具有外部联系。 zneak's answer, EDIT 2,有一个电子邮件主题与clang开发人员确认foo<Bar>应该有外部链接。

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

  

非类型,非模板模板参数 template-argument 应为以下之一:...   

  • 一个常量表达式(5.19),用于指定具有静态存储持续时间和外部或内部链接的对象的地址,或具有外部或内部链接的函数,包括函数模板和函数 template-id < / i>但排除非静态类成员,表示(忽略括号)为& id-expression ,但如果名称引用&,则可省略foo<bar>函数或数组,如果相应的 template-parameter 是引用,则应省略; ...

  • 最相关的子弹是第三颗子弹。由于{{1}}具有外部链接,因此将其作为非类型模板参数传递应该没问题。

    答案 1 :(得分:3)

    我迟到了,但标准说类型本地函数没有链接(§3.5:8):

      

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

    同样的部分继续说:

      

    除非

    ,否则不带链接的类型不能用作具有外部链接的变量或函数的类型      
        
    • 该实体具有C语言链接(7.5)或
    •   
    • 实体在未命名的命名空间(7.3.1)中声明,或
    •   
    • 该实体不是使用过的(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 The guys at clang confirm it's a bug.