10

我有一些非常简单的 ( 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:需要明确的是,这不是这个问题的重复,因为在 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;
}
4

2 回答 2

3

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

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

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

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

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

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

  • 最相关的项目符号是第三个项目符号。由于foo<bar>具有外部链接,因此将其作为非类型模板参数传递应该没问题。

    于 2013-08-28T23:04:09.967 回答
    3

    我迟到了,但标准说类型本地函数没有链接(§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 所指出的,名称也是在同一个翻译单元中定义的,所以我不确定如何看待这个。


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

    于 2013-08-29T00:32:33.323 回答