nullポインタ引数と不可能な事後条件を使用して標準例外を構築します


9

次のプログラムを検討してください。

#include<stdexcept>
#include<iostream>

int main() {
    try {
        throw std::range_error(nullptr);
    } catch(const std::range_error&) {
        std::cout << "Caught!\n";
    }
}

libstdc ++を使用したGCCおよびClang std::terminateは、メッセージを表示してプログラムを呼び出し、中止します

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

例外の構築時にlibc ++のClangがsegfaultします。

godboltを参照してください。

コンパイラは標準に準拠していますか?標準の関連セクション[diagnostics.range.error](C ++ 17 N4659)は言っていstd::range_error有するconst char*好まれるべきであるコンストラクタのオーバーロードconst std::string&オーバーロード。このセクションでは、コンストラクターの前提条件についても述べておらず、事後条件についてのみ述べています。

事後条件strcmp(what(), what_­arg) == 0

what_argnullポインターの場合、この事後条件は常に未定義の動作を持っています。つまり、これは私のプログラムにも未定義の動作があり、両方のコンパイラーが準拠して動作することを意味しますか?そうでない場合、標準でそのような不可能な事後条件をどのように読むべきですか?


考え直してみれば、それは私のプログラムにとって未定義の動作を意味しているに違いないと思います。そうしないと、(有効な)ヌル終了文字列を指さないポインタも許可されるため、明らかに意味がありません。

それで、それが真実であると仮定して、標準がこの未定義の動作をどのように暗示するかにもっと質問を集中したいと思います。これは、事後条件が不可能であることから、呼び出しにも未定義の動作があるか、または前提条件が単に忘れられたかということですか?


この質問に触発されました


std :: range_errorは参照によって物事を保存することが許可されているように聞こえるので、私は驚かないでしょう。が渡されたwhat()ときに呼び出すと、nullptrおそらく問題が発生します。
チップスター

@Chipster正確にはどういう意味かはわかりませんが、もう一度考えてみると、未定義の振る舞いだと思います。私は質問を編集して、標準的な表現が未定義の動作をどのように暗示するに焦点を当てています。
クルミ

nullptrが渡された場合 what()、値を取得するためにある時点で逆参照する必要があると思います。それはを逆参照することになりますnullptr。これは、せいぜい問題があり、クラッシュすることが確実であり、最悪です。
チップスター

しかし、私は同意します。未定義の動作でなければなりません。ただし、理由を説明する適切な答えにそれを入れることは私のスキルを超えています。
チップスター

strcmpの値を記述するために使用されるため、引数が有効なC文字列を指すことが前提条件であることが意図されていると思いますwhat_arg。とにかく、C標準の関連するセクションでこのように述べています。これはの仕様で参照されてい<cstring>ます。もちろん、言い回しはより明確になるかもしれません。
LF

回答:


0

ドキュメントから:

std :: range_errorのコピーは例外のスローを許可されていないため、このメッセージは通常、個別に割り当てられた参照カウント文字列として内部的に保存されます。これは、std :: string &&を使用するコンストラクターがない理由でもあります。とにかくコンテンツをコピーする必要があります。

これは、segfaultが発生する理由を示しています。APIは実際にsegfaultを実際の文字列として扱います。一般にcppでは、何かがオプションである場合、必要のないものを取らないオーバーロードされたコンストラクター/関数が存在します。そのnullptrため、オプションであることを文書化していない関数を渡すことは、未定義の動作になります。通常、APIはC文字列を除いてポインタを取りません。したがって、私見では、を期待する関数にnullptrを渡すことは、const char *未定義の動作になると想定しても安全です。新しいAPIはstd::string_viewこれらのケースを好むかもしれません。

更新:

通常、C ++ APIがNULLを受け入れるためのポインターを受け取ると想定するのは公平です。ただし、C文字列は特殊なケースです。までstd::string_view、それらを効率的に渡すためのより良い方法はありませんでした。一般に、APIがを受け入れるconst char *場合、有効なC文字列である必要があると想定する必要があります。つまり、char'\ 0'で終了するsのシーケンスへのポインタ。

range_errorはポインタがそうでないnullptrことを検証できますが、 '\ 0'で終了するかどうかは検証できません。したがって、検証は行わない方がよいでしょう。

標準の正確な表現はわかりませんが、この前提条件はおそらく自動的に想定されています。


-2

これは基本的な質問に戻りますが、nullptrからstd :: stringを作成してもよいですか?そしてそれは何をすべきですか?

www.cplusplus.comは言う

sがnullポインターの場合、n == nposの場合、または[first、last)で指定された範囲が無効な場合、未定義の動作が発生します。

そうするとき

throw std::range_error(nullptr);

実装は次のようなものを作ろうとする

store = std::make_shared<std::string>(nullptr);

これは未定義です。私はバグと見なします(標準の実際の文言を読んだことがない場合)。代わりに、自由な開発者は次のようなものを作ることができたでしょう

if (what_arg)
  store = std::make_shared<std::string>(nullptr);

しかし、キャッチャーはnullptrをチェックインする必要what();があります。そうしないと、そこでクラッシュします。したがって、std::range_errorは他の一部の言語と同様に、空の文字列または「(nullptr)」を割り当てる必要があります。


「これは基本的な質問に戻ります。nullptrからstd :: stringを作成してもよいですか?」—そうは思いません。
Konrad Rudolph

私は、例外を救うために持っていることをどこでも標準を指定しないと思うstd::stringstd::string、コンストラクタはオーバーロードの解決によって選択されるべきではありません。
クルミ

const char *過負荷は、オーバーロードの解決によって選択されている
MM
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.