clang error: non-type template argument refers to

2020-08-17 06:53发布

I have some very simple (C++11) code which the latest clang (version 3.4 trunk 187493) fails to compile, but GCC compiles fine.

The code (below) instantiates the function-template foo with the function-local type Bar and then tries to use its address as a non-type template parameter for the class-template 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 emits the following error:

error: non-type template argument refers to function 'foo' that does not have linkage

However, if I move type Bar to global scope (by taking it out of the function), then clang compiles it fine, proving the issue is with the type being function-local.

So is clang correct to emit this error, or does the standard not support this (in which case GCC is being too lenient by allowing it)?


EDIT #1 : To be clear, this is not a duplicate of this question since the 'cannot use local types as template parameters' restriction was removed in C++11. However, it's still unclear if there are linkage implications involved with using a local type, and whether clang is correct or not in emitting this error.


EDIT #2 : It has been determined that clang was correct to emit the error for the above code (see answer from @jxh), but that it incorrectly also emits an error for the following code (with using declaration moved from foo<Bar>() scope to main() scope):

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条回答
forever°为你锁心
2楼-- · 2020-08-17 07:32

By definition of no linkage in C++.11 §3.5 Program and linkage ¶2, I originally believed foo<Bar> has no linkage since it cannot be referred to by name by any other scope except that which defined the type Bar (ie, main()). However, this is not correct. This is because the definition of a name with external linkage is described as:

When a name has external linkage, the entity it denotes can be referred to by names from scopes of other translation units or from other scopes of the same translation unit.

And for a template function, this will always be the case. This is because there is one other scope from which the name can be referred. Namely, the template function can refer to itself. Therefore, foo<Bar> has external linkage. zneak's answer, EDIT 2, has an e-mail thread with the clang developers confirming that foo<Bar> should have external linkage.

Thus, from C++.11 §14.3.2 Template non-type arguments ¶1:

A template-argument for a non-type, non-template template-parameter shall be one of: ...

  • a constant expression (5.19) that designates the address of an object with static storage duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as &id-expression, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; ...

  • The most relevant bullet is the third bullet. Since foo<bar> has external linkage, passing it as a non-type template-parameter should be fine.

    查看更多
    \"骚年 ilove
    3楼-- · 2020-08-17 07:39

    I'm late to the party, but standard says that type-local functions don't have linkage (§3.5:8):

    Names not covered by these rules have no linkage. Moreover, except as noted, a name declared at block scope (3.3.3) has no linkage.

    Same section goes on to say:

    A type without linkage shall not be used as the type of a variable or function with external linkage unless

    • the entity has C language linkage (7.5), or
    • the entity is declared within an unnamed namespace (7.3.1), or
    • the entity is not odr-used (3.2) or is defined in the same translation unit.

    And, as a matter of fact, Clang will allow this:

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

    And will reject it without the anonymous namespace.


    EDIT 1: As jxh notes, the names are also defined in the same translation unit, so I'm not sure what to think of this one.


    EDIT 2: The guys at clang confirm it's a bug.

    查看更多
    登录 后发表回答