1. ホーム
  2. c++

[解決済み] const std::string& を受け取る関数が 0 を受け取らないようにする。

2023-01-02 17:25:06

質問

千の言葉に匹敵する。

#include<string>
#include<iostream>

class SayWhat {
    public:
    SayWhat& operator[](const std::string& s) {
        std::cout << s << "\n";
        return *this;
    }
};

int main() {
    SayWhat ohNo;
    // ohNo[1]; // Does not compile. Logic prevails.
    ohNo[0]; // you didn't! this compiles.
    return 0;
}

文字列を受け入れるブラケット演算子に数値0を渡しても、コンパイラは文句を言いません。その代わり、これはコンパイルされ、withメソッドへのエントリーの前に失敗します。

terminate called after throwing an instance of 'std::logic_error'
  what():  basic_string::_S_construct null not valid

参考までに

> g++ -std=c++17 -O3 -Wall -Werror -pedantic test.cpp -o test && ./test
> g++ --version
gcc version 7.3.1 20180303 (Red Hat 7.3.1-5) (GCC)

私の推測

コンパイラは暗黙のうちに std::string(0) コンストラクタを使用してメソッドに入るため、正当な理由なく同じ問題 (上記のエラーでググってみてください) が発生します。

質問

APIユーザがこれを感じず、コンパイル時にエラーが検出されるように、クラス側でこれを修正する方法はありますか?

つまり、オーバーロードを追加する

void operator[](size_t t) {
    throw std::runtime_error("don't");
}

は良い解決策ではありません。

どのように解決するのですか?

原因 std::string(0) が有効である理由は 0 がヌルポインタ定数であるためです。つまり、0は文字列コンストラクタがポインタを取ることにマッチします。すると、このコードは、NULLポインタを std::string .

リテラルのみ 0 の中の実行時値であれば、ヌルポインタ定数として解釈されます。 int の実行時値であれば、この問題は発生しません(オーバーロードの解決は int の変換を探すことになるため)。また、リテラル 1 は問題ではありません。 1 はヌルポインタ定数ではないためです。

これはコンパイル時の問題(リテラルが無効な値)なので、コンパイル時にキャッチすることができます。このフォームのオーバーロードを追加してください。

void operator[](std::nullptr_t) = delete;

std::nullptr_t のタイプは nullptr . そして、それは にもマッチします。 ヌルポインタ定数、それが 0 , 0ULL または nullptr . そして、この関数は削除されているので、オーバーロードの解決時にコンパイルタイムエラーを引き起こします。