コンパイル時に解決されない文字列リテラルとの比較


8

私は最近、次のようなものを見つけました。

#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";
}

関数は明らかにコメントが示唆することを行いません。しかし、それはここでのポイントではありません。これはifステートメントで2つ以上のOR条件を使用できますか?あなたが関数を適切に書く方法を私は完全に知っているので!


コンパイラがこのスニペットをどのように処理するのか疑問に思い始めました。私の最初の直感は、これが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

だから私の中心的な質問は:コンパイラが最初のスニペットで同じ最適化を行うことを許可しない見逃したものはありますか?


1これ".foo"sでは、コンパイラはa std::stringbool;-) に変換したくないので、コンパイルすらしません。


編集する

次のコードも、「適切に」最適化されreturn true;ます。

#include <string>

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

3
ええと、string::compare(const char*)コンパイラが排除operator==(string, string)しない(それがない)副作用がありますか?可能性は低いと思われますが、コンパイラーはmov eax, 1 ret、最初のスニペットであっても、結果が常に真(である)であると既に判断していました。
Max

2
おそらく、operator==(string const&, string const&)あるnoexcept一方でoperator==(string const&, char const*)はないのですか?これをさらに掘り下げる時間はありません。
AProgrammer

@MaxLanghof順序をfoo || ext == ".bar"に変更すると、呼び出しは最適化されます(編集を参照)。それはあなたの理論と矛盾していますか?
AlexV

2
@AlexVどういう意味かわかりません。式の短絡は、a || bbaが次の場合のみ式を評価する」ことを意味しfalseます。実行時またはコンパイル時と直交しています。(最適化されているかどうかにかかわらず)右側が評価されないため、副作用がある場合でも、true || foo()をに最適化できます。ただし、コンパイラーが呼び出しに観察可能な副作用がないことを証明できない限り、最適化できません。truefoo()foo() || truetruefoo()
Max Langhof

1
提供されているコンパイラエクスプローラのリンクを使用して、[バイナリにコンパイルして出力を逆アセンブルする]オプションをオンにxor eax,eaxすると、文字列比較関数を呼び出すオプションがないにもかかわらず、突然コンパイルされます。何を作ればいいのか分かりません。
ダニエルH

回答:


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";
}

確かに、カスタムタイプはプレーンよりも簡単に最適化することはできません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

ライブでご覧ください!


マインドブロー!

それで、私の「カスタム」文字列型との違いは何std::stringですか?

小さな文字列の最適化

少なくともGCCでは、Small String Optimizationは実際にはlibstdc ++のバイナリーにコンパイルされています。つまり、関数のコンパイル中、コンパイラはこの実装にアクセスできないため、副作用がないかどうかを知ることができません。このため、compare(char const*)away への呼び出しを最適化できません。SSOはplainに対してのみ実装されているため、「カスタム」クラスにはこの問題はありませんstd::string

ところで、でコンパイルすると-std=c++2aコンパイラはそれを最適化します。残念ながら、C ++ 20にまだ精通していないので、これを可能にした変更についてはまだわかりません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.