【问题标题】:Comparing against string literal not resolved at compile time与编译时未解析的字符串文字进行比较
【发布时间】:2020-03-28 05:27:17
【问题描述】:

我最近发现了类似于以下几行的内容:

#include <string>

// test if the extension is either .bar or .foo
bool test_extension(const std::string& ext) {
    return ext == ".bar" || ".foo";
    // it obviously should be
    // return ext == ".bar" || ext == ".foo";
}

该功能显然没有按照评论的建议进行。但这不是重点。请注意,这不是 Can you use 2 or more OR conditions in an if statement? 的重复,因为我完全了解您将如何正确编写函数!


我开始怀疑编译器会如何处理这个 sn-p。我的第一个直觉是这基本上会被编译成return true;。将该示例插入godbolt,表明GCC 9.2 和clang 9 都没有通过优化-O2 进行此优化。

但是,将代码更改为1

#include <string>

using namespace std::string_literals;

bool test_extension(const std::string& ext) {
    return ext == ".bar"s || ".foo";
}

似乎可以解决问题,因为程序集现在本质上是:

mov     eax, 1
ret

所以我的核心问题是:我错过了什么不允许编译器在第一个 sn-p 上进行相同的优化?


1使用".foo"s 这甚至无法编译,因为编译器不想将std::string 转换为bool ;-)


编辑

以下代码也“正确”优化为return true;

#include <string>

bool test_extension(const std::string& ext) {
    return ".foo" || ext == ".bar";
}

【问题讨论】:

  • 嗯,string::compare(const char*) 是否有一些编译器无法消除的副作用(operator==(string, string) 没有)?似乎不太可能,但编译器确实已经确定结果始终为真(也有mov eax, 1ret),即使对于第一个 sn-p。
  • 可能是因为operator==(string const&amp;, string const&amp;)noexceptoperator==(string const&amp;, char const*) 不是?我现在没有时间进一步挖掘。
  • @MaxLanghof 将顺序更改为foo || ext == ".bar" 时,调用已被优化掉(见编辑)。这是否与您的理论相矛盾?
  • @AlexV 我不确定这应该是什么意思。表达式a || b 的短路意味着“仅当表达式afalse 时才评估表达式b”。它与运行时或编译时正交。 true || foo() 可以优化为 true,即使 foo() 有副作用,因为(无论是否优化)右侧永远不会被评估。但是foo() || true 不能优化为true,除非编译器可以证明调用foo() 没有可观察到的副作用。
  • 当我获取您提供的编译器资源管理器链接并检查“编译为二进制并反汇编输出”选项时,它会突然编译为xor eax,eax,即使没有该选项它也会调用字符串比较函数。我不知道该怎么做。

标签: c++ string c++14 compiler-optimization


【解决方案1】:

这会让你更加困惑:如果我们创建一个自定义字符类型 MyCharT 并使用它来创建我们自己的自定义 std::basic_string,会发生什么?

#include <string>

struct MyCharT {
    char c;
    bool operator==(const MyCharT& rhs) const {
        return c == rhs.c;
    }
    bool operator<(const MyCharT& rhs) const {
        return c < rhs.c;
    }
};
typedef std::basic_string<MyCharT> my_string;

bool test_extension_custom(const my_string& ext) {
    const MyCharT c[] = {'.','b','a','r', '\0'};
    return ext == c || ".foo";
}

// Here's a similar implementation using regular
// std::string, for comparison
bool test_extension(const std::string& ext) {
    const char c[] = ".bar";
    return ext == c || ".foo";
}

当然,自定义类型不能比普通的 char 更容易优化,对吧?

这是生成的程序集:

test_extension_custom(std::__cxx11::basic_string<MyCharT, std::char_traits<MyCharT>, std::allocator<MyCharT> > const&):
        mov     eax, 1
        ret
test_extension(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&):
        sub     rsp, 24
        lea     rsi, [rsp+11]
        mov     DWORD PTR [rsp+11], 1918984750
        mov     BYTE PTR [rsp+15], 0
        call    std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::compare(char const*) const
        mov     eax, 1
        add     rsp, 24
        ret

See it live!


那么,我的“自定义”字符串类型和std::string 有什么区别?

小字符串优化

至少在 GCC 上,Small String Optimization 实际上被编译为 libstdc++ 的二进制文件。这意味着,在编译您的函数期间,编译器无法访问此实现,因此它无法知道是否有任何副作用。因此,它无法优化对compare(char const*) 的调用。我们的“自定义”类没有这个问题,因为 SSO 仅针对普通的 std::string 实现。

顺便说一句,如果您使用 -std=c++2athe compiler does optimize it away 编译。不幸的是,我对 C++ 20 还不够了解,还不知道是什么变化使这成为可能。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-04
    • 2022-11-13
    • 2017-02-20
    相关资源
    最近更新 更多