c++templatesstrcmpnon-type

Why does strcmp() in a template function return a different value?


I am reading again "C++ Primer, 5th edition". In chapter 16 about templates, there is an example of "template Non-type parameters":

template<unsigned N, unsigned M>
int compare(const char (&p1)[N], const char (&p2)[M])
{
    return strcmp(p1, p2);
}

int main()
{

    cout << compare("hi", "mom") << endl;
    cout << strcmp("hi", "mom") << endl;


    std::cout << "\ndone!\n";
}

output:

-5
-1

What is the problem in the code? And why are the two giving different values for the same arguments?


Solution

  • This is a compiler optimization applied when strcmp is passed literal parameters, even on -O0. See this compiler explorer link: https://godbolt.org/z/T4EKxr

    #include <cstring>
    
    template<unsigned N, unsigned M>
    int compare(const char (&p1)[N], const char (&p2)[M])
    {
        return strcmp(p1, p2);
    }
    
    int a() {
        return compare("hi", "mom");
    }
    
    int b() {
        return strcmp("hi", "mom");
    }
    

    The generated assembly:

    .LC0:
            .string "mom"
    .LC1:
            .string "hi"
    a():
            push    rbp
            mov     rbp, rsp
            mov     esi, OFFSET FLAT:.LC0
            mov     edi, OFFSET FLAT:.LC1
            call    int compare<3u, 4u>(char const (&) [3u], char const (&) [4u])
            pop     rbp
            ret
    b():
            push    rbp
            mov     rbp, rsp
            mov     eax, -1
            pop     rbp
            ret
    int compare<3u, 4u>(char const (&) [3u], char const (&) [4u]):
            push    rbp
            mov     rbp, rsp
            sub     rsp, 16
            mov     QWORD PTR [rbp-8], rdi
            mov     QWORD PTR [rbp-16], rsi
            mov     rdx, QWORD PTR [rbp-16]
            mov     rax, QWORD PTR [rbp-8]
            mov     rsi, rdx
            mov     rdi, rax
            call    strcmp
            leave
            ret
    

    As we can see, for b() above, gcc is optimizing the call to strcmp to just a -1, whereas it actually calls strcmp for a(). This is valid behavior, as strcmp returns:

    Negative value if lhs appears before rhs in lexicographical order.

    Zero if lhs and rhs compare equal.

    Positive value if lhs appears after rhs in lexicographical order.

    -1 is negative.

    If we turn on optimizations, gcc will similarly optimize a().