【问题标题】:Why does strcmp() in a template function return a different value?为什么模板函数中的 strcmp() 返回不同的值?
【发布时间】:2021-02-05 19:42:36
【问题描述】:

我正在再次阅读“C++ Primer,第 5 版”。在第16章关于模板,有一个“模板非类型参数”的例子:

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";
}
  • 众所周知,strcmp() 比较两个字符串并返回0 相等,如果str1 大于str2 则返回正值,如果str1 小于str2 则返回负值,这就是我在main() 调用strcmp() 时得到的。

  • 问题出在在模板函数中调用strcmp() 的书中示例中,所以当我运行程序时,我得到:

输出:

-5
-1

代码有什么问题?为什么两者对相同的参数给出不同的值?

【问题讨论】:

    标签: c++ templates strcmp non-type


    【解决方案1】:

    这是在 strcmp 传递文字参数时应用的编译器优化,即使在 -O0 上也是如此。请参阅此编译器资源管理器链接: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");
    }
    

    生成的程序集:

    .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
    

    正如我们所见,对于上面的b(),gcc 正在优化对strcmp 的调用,使其仅是-1,而实际上它为a() 调用strcmp。这是有效的行为,strcmp 返回:

    如果 lhs 按字典顺序出现在 rhs 之前,则为负值。

    如果 lhs 和 rhs 比较相等,则为零。

    如果 lhs 按字典顺序出现在 rhs 之后,则为正值。

    -1 是否定的。

    如果我们开启优化,gcc 将同样优化a()

    【讨论】:

    • 真的很棒!我很感激。在我的代码块上,我打开了 [-o1] 的优化并将目标设为 Release 并重新构建,并且我得到了相同的结果:-1 -1 所以这是编译器优化的问题。谢谢!
    • @Maestro - 顺便说一句,这是应用程序中 Liskov 替换原则(用于函数)的一个示例,由编译器应用:它可以产生不同的实现,返回不同的结果,但仍然在其中规范 - 如果您正确编写了代码(即,您没有依赖特定值),它不应该引起任何意外/错误。
    猜你喜欢
    • 1970-01-01
    • 2021-12-22
    • 1970-01-01
    • 2014-11-02
    • 2020-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多