関数でconst std :: string&が0を受け入れないようにする


97

千の言葉に値する:

#include<string>
#include<iostream>

class SayWhat {
    public:
    SayWhat& operator[](const std::string& s) {
        std::cout<<"here\n"; // To make sure we fail on function entry
        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を渡しても、コンパイラは文句を言わない。代わりに、これはコンパイルされ、メソッドに入る前に失敗します:

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

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


2
コンパイルされたコード、Visual StudioのohNo [0]で例外 "0xC0000005:アクセス違反の読み取り場所0x00000000"を
スロー

5
引数operator[]()を受け入れるプライベートオーバーロードを宣言し、intそれを定義しないでください。
ピーター

2
@ピーターそれは私が持っていたものよりもまだ優れているリンカーエラーであることに注意してください。
kabanus

5
@kabanus上記のシナリオでは、オペレーターがプライベートであるため、コンパイラエラーになります。クラス内で呼び出された場合にのみリンカーエラーが発生する...
Aconcagua

5
@Peterこれは、C ++ 11が利用できないシナリオで特に興味深いものです。これら今日でも存在します(実際、私は対処しなければならないプロジェクトに参加しており、いくつかの新機能が不足しています... )。
アコンカグア

回答:


161

理由std::string(0)は有効ですが、これは0nullポインター定数であるためです。したがって、0は、ポインターを受け取る文字列コンストラクターと一致します。次に、nullポインタをに渡してはならないという前提条件に反して、コードが実行されstd::stringます。

0それがランタイム値である場合、リテラルのみがnullポインター定数として解釈されますint(この場合、オーバーロードの解決ではint変換が検索されるため)。はnullポインタ定数ではない1ため、文字どおりの問題でも1ありません。

これはコンパイル時の問題(文字通り無効な値)であるため、コンパイル時に検出できます。このフォームのオーバーロードを追加します。

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

std::nullptr_tのタイプですnullptr。そして、それは一致する任意のそれは、ヌルポインタ定数を00ULLまたはnullptr。また、関数が削除されているため、オーバーロードの解決中にコンパイル時エラーが発生します。


これは断然最高の解決策です。NULLポインタをオーバーロードすることができることを完全に忘れていました。
kabanus

Visual Studioでは、 "ohNo [0]"でもnull値の例外がスローされます。std :: stringクラス固有の実装を意味しますか?
TruthSeeker

@pmpスローされるもの(もしあれば)は実装固有ですが、重要なのは、文字列がそれらすべてのNULLポインタであることです。このソリューションでは、例外部分に到達せず、コンパイル時に検出されます。
kabanus

18
@pmp- std::stringのコンストラクタにnullポインタを渡すことは、C ++標準では許可されていません。これは未定義の動作なので、MSVCは(例外をスローするなど)好きなように実行できます。
StoryTeller-Unslander Monica

26

1つのオプションは、整数引数を受け入れるのprivateオーバーロードを宣言し、operator[]()それを定義しないことです。

このオプションはvoid operator[](std::nullptr_t) = delete、C ++ 11から有効なオプションとは異なり、すべてのC ++標準(1998以降)で機能します。

作成operator[]()Aのprivateメンバーはあなたの例で診断可能エラーが発生します ohNo[0]発現がメンバ関数によってまたは使用されていない限り、friendクラスの。

その式がメンバー関数またはfriendクラスから使用される場合、コードはコンパイルされますが、関数が定義されていないため、通常はビルドが失敗します(たとえば、未定義の関数によるリンカーエラー)。

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