与编译时未解析的字符串文字进行比较

Ale*_*exV 8 c++ string compiler-optimization c++14

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

#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";
}
Run Code Online (Sandbox Code Playgroud)

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


我开始想知道编译器会如何处理这个片段。我的第一个直觉是这将被编译为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";
}
Run Code Online (Sandbox Code Playgroud)

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

mov     eax, 1
ret
Run Code Online (Sandbox Code Playgroud)

所以我的核心问题是:我是否遗漏了什么不允许编译器对第一个片段进行相同的优化?


1随着".foo"s这甚至不会编译,因为编译器不希望将转换std::stringbool;-)


编辑

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

#include <string>

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

Cás*_*nan 3

这会让你更加困惑:如果我们创建一个自定义 char 类型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";
}
Run Code Online (Sandbox Code Playgroud)

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

这是最终的组装结果:

#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";
}
Run Code Online (Sandbox Code Playgroud)

现场观看!


惊呆了!

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

小字符串优化

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

顺便说一句,如果您使用 进行编译-std=c++2a编译器会对其进行优化。不幸的是,我对 C++ 20 还不够了解,还不知道哪些变化使这成为可能。