3
votes

I have a static function defined within a struct defined within a function. I want to get a template function pointer to the inner function.

Consider the following example:

template <class Func, Func GetToken>
struct token_user {
    void foo () { GetToken(); } // Do something with token.
};

struct generator_out {
    constexpr static const auto get_token() {return 0;}
};

int main() {
    struct generator_in {
        constexpr static const auto get_token() {return 0;}
    };
    // Works fine
    token_user<decltype(&generator_out::get_token), &generator_out::get_token>(); 
    // Fails with GCC, works with clang and msvc
    token_user<decltype(&generator_in::get_token), &generator_in::get_token>();
}

I tested this with my local MSVC 2017 Compiler and also with the clang 6.0 and gcc 8.1 compiler on wandbox.org. MSVC and clang work, gcc doesn't.

Who is correct? Are clang and msvc too forgiving and bend the c++ standard or has gcc simply not implemented this yet? (Or is it maybe even a bug?) What does the standard say about this?

EDIT: Error message from gcc:

prog.cc: In function 'int main()':
prog.cc:15:76: error: 'main()::generator_in::get_token' is not a valid template argument for type 'const int (*)()' because 'static constexpr const auto main()::generator_in::get_token()' has no linkage
 token_user<decltype(&generator_in::get_token), &generator_in::get_token>(); // Fails with GCC, works with clang and msvc
1
@OlivierSohn order of specifiers doesn't matter and is simply a matter of personal preference. - Chartas
@VTT I'm not sure if it even is an error. Might be that msvc and clang just support an experimental feature. - Chartas
@C.Esch It is an error if gcc prints error:. Also standard defines that in-function definitions have no linkage, but i'm not sure why would it prevent gcc from using pointer to function as a template parameter. - user7860670
maybe linked to this - Tyker
clang 6.0 errors, too, is std < c++17, but is ok when std=c++17 - Olivier Sohn

1 Answers

2
votes

GCC is right, generator_in::get_token has no linkage and cannot be used outside the scope in which it is declared, and thus cannot be used as an argument to instantiate token_user.

See [basic.link]/8 and /9 (I'm including only relevant parts):

... except as noted, a name declared at block scope has no linkage.

Example:

void f() {
  struct A { int x; };  // no linkage
}

This is important because member functions have the linkage of the class. [basic.link]/5:

... a member function, static data member, a named class or enumeration of class scope, or an unnamed class or enumeration defined in a class-scope typedef declaration such that the class or enumeration has the typedef name for linkage purposes ([dcl.typedef]), has the same linkage, if any, as the name of the class of which it is a member.

So both generator_in and generator_in::get_token have no linkage.

And finally: [basic.link]/2.3:

— When a name has no linkage, the entity it denotes cannot be referred to by names from other scopes.