0

I am confused by the function pointer of the template function.

See

#include <cstdio>

class A
{
    public:
        A(){}
        ~A(){}
        void command()
        {
            printf("Cmd");   
        }
};

template<class T>
void cmd_create()
{
    T t;
    t.command();
};

int main()
{
    typedef void(*Funcptr)();
    Funcptr f1 = &cmd_create<A>;
    f1();
    return 0;
}

The program output Cmd.

The assembly (-O3) shows that

    .file   "test.cpp"
    .text
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "Cmd"
    .section    .text.startup,"ax",@progbits
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB38:
    .cfi_startproc
    leaq    .LC0(%rip), %rsi
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $1, %edi
    xorl    %eax, %eax
    call    __printf_chk@PLT
    xorl    %eax, %eax
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE38:
    .size   main, .-main
    .ident  "GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0"
    .section    .note.GNU-stack,"",@progbits

Obviously, &cmd_create<A> returns the A::command.

The cmd_create is defined as an empty return (void), however, the code still gets the A::command in such a weird way.

Thank you for your answer!

10
  • 1
    What do you mean by Obviously, &cmd_create<A> returns the A::command.? &cmd_create<A>1 gives you a pointer to the function cmd_create where T gets replaced with concrete type of A. That function call A::command. Nothing is returned from either function. Commented Apr 8, 2022 at 15:13
  • 2
    That's equivalent to modern compilers being very good at inlining code – all the "fluff" has disappeared, but the program has the same observable behaviour. Commented Apr 8, 2022 at 15:16
  • 1
    The compiler is smart enough to tell that both A and cmd_create are irrelevant, the only observable result is calling printf. Godbolt has easier to digest assembly: godbolt.org/z/qKhdvoT3q Commented Apr 8, 2022 at 15:17
  • 2
    Aha, what you are seeing is called function inlining. Basically, the compiler is smart enough to know that instead of calling the function at run time, it can just insert the functions code directly into the call site so no function call happens at run time. Commented Apr 8, 2022 at 15:19
  • 2
    @Mangoccc A very big part of C++ is performance, so much so that we have a thing called the as-if rule, which states that as long as the observable behavior of the program does not change, the compiler is allowed to transform your code into whatever it wants. In this case that means inlining the function to save some function call costs. Other times it could be optimizing a loop out of the code as it can either compute the value the loop would produce or it sees the loop has no observable effect and optimizes it away. Commented Apr 8, 2022 at 15:35

1 Answer 1

1

Obviously, &cmd_create returns the A::command.

I am not fluent with assembly, but obviously you misunderstand the code ;).

&cmd_create<A> is a pointer to the function cmd_create<A>. This function pointer is assigned to f1. The pointer is of type void(*)(), pointer to function returning void and no arguments. No function in the code returns anything (if we do not count printf or main).

f1(); calls the function pointed to by f1. That is: cmd_create<A>. The function creates an object of type A and calls its command member function which prints the output you see on the screen.

Enabling optimizations means that the compiler emits something that has the same observable behavior. It could emit the same as it would for int main() { printf("Cmd"); }.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.