Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ template not compiling when passing through function signature

Tags:

c++

templates

Consider the following, minimal example (compiles without #1 and #2):

void foo(void)
{ }

template<typename T> class Stage2;

template<typename Ret, typename... Args>
struct Stage2<Ret (Args...)>
{
    template<Ret (*func)(Args...)>
    static void foobar(void)
    { /* Do something */ }
};

template<typename FuncType>
struct Stage1
{
    template<FuncType func>
    static void bar(void)
    {
        Stage2<FuncType>::foobar<func>();       // #1, Not working
        Stage2<decltype(func)>::foobar<func>(); // #2, Not working
        Stage2<void()>::foobar<func>();         // #3, Working
    }
};

int main(void)
{
    Stage1<decltype(foo)>::bar<foo>();
    return 0;
}

Why does it not compile with #1 and #2, whereas it compiles just fine with #3? In my opinion, #3 should be equivalent to the others as long as foo has the signature void(), which it does in this example. Even the compiler tells me, that FuncType is in fact void() (see below).

Error message (the same for #1 and #2):

main.cpp: In static member function ‘static void Stage1<FuncType>::bar()’:
main.cpp:21:40: error: expected primary-expression before ‘)’ token
         Stage2<FuncType>::foobar<func>();       // #1, Not working
                                        ^
main.cpp: In instantiation of ‘static void Stage1<FuncType>::bar() [with FuncType func = foo; FuncType = void()]’:
main.cpp:29:37:   required from here
main.cpp:21:33: error: invalid operands of types ‘<unresolved overloaded function type>’ and ‘void (*)()’ to binary ‘operator<’
         Stage2<FuncType>::foobar<func>();       // #1, Not working
         ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~

What am I missing here? I'm using g++ 7.2.0.

Note: I'm not really interested if this is useful in any way, I'd just like to know why it doesn't compile because it makes no sense to me.

like image 300
Shadowigor Avatar asked Oct 28 '17 21:10

Shadowigor


1 Answers

Basically, what's going on is this:

Stage2<FuncType>::foobar<func>();

contains a dependent name (depending on FuncType), therefore you have to follow the correct C++-syntax for calling member templates (thus the syntax error messages), which is

Stage2<FuncType>::template foobar<func>();

Note that this does not apply for Stage2<void()>::foobar<func>(); as there is no dependent name involved.

The same applies to Stage2<decltype(func)>::foobar<func>();, but this alone still won't fix it as there is some tricky obstacle. According to §14.1.8 [temp.param],

A non-type template-parameter of type “array of T” or “function returning T” is adjusted to be of type “pointer to T” or “pointer to function returning T”, respectively.

decltype(func) will be void(*)() instead of void() (even though FuncType was specified to be void()), so no function type but pointer to function type will be passed as template parameter to Stage2 for which there is no specialization provided (as Stage2<Ret (Args...)> and Stage2<Ret (*)(Args...)> are not the same), thus falling back to the default template declaration, finally yielding a "use of incomplete type" error.

like image 93
Jodocus Avatar answered Oct 13 '22 00:10

Jodocus