NULL
からstd::string
を初期化するcharポインタは未定義の動作だと思います。したがって、ここにコンストラクタの代替バージョンがあります。ここで、mStdString
はstd::string
型のメンバー変数です。
void MyClass::MyClass(const char *cstr) :
mStdString( cstr ? cstr : "")
{}
void MyClass::MyClass(const char *cstr) :
mStdString(cstr ? std::string(cstr) : std::string())
{}
void MyClass::MyClass(const char *cstr)
{
if (cstr) mStdString = cstr;
// else keep default-constructed mStdString
}
編集、class MyClass
内のコンストラクター宣言:
MyClass(const char *cstr = NULL);
どれがNULL
ポインタからstd::string
を初期化するための最良または最も適切な方法は、これらのうちのどれか、またはおそらく他の何かであり、なぜですか? C++標準ごとに異なりますか?通常のリリースビルド最適化フラグを想定しています。
個人的な意見だけでなく、方法が正しい方法である理由の説明付きの回答、または参照リンク付きの回答(回答が「問題ではない」場合にも適用されます)を探しています)必要な場合は、少なくともコメントにするだけです)
最後のものは、可能であれば初期化を使用しないため、ばかげています。
最初の2つは意味的に完全に同一です(c_str()
メンバー関数と考えてください)。最初のバージョンが最も直接的で慣用的であり、最も読みやすいので、最初のバージョンをお勧めします。
(_std::string
_にconstexpr
のデフォルトコンストラクタがある場合、は意味上の違いがありますが、そうではありません。それでも可能std::string()
はstd::string("")
とは異なりますが、あまり意味がないように見えるため、これを行う実装はわかりません。一方、最近人気のある小さな文字列の最適化は、両方のバージョンがおそらく動的割り当てをしないことを意味します。)
更新:@Jonathanが指摘するように、2つの文字列コンストラクターはおそらく異なるコードを実行し、それが重要な場合(実際にはそうではありませんが)、4番目のバージョン:
_: cstr ? cstr : std::string()
_
可読性とデフォルト構築の両方。
2回目の更新:ただし、_cstr ? cstr : ""
_をお勧めします。以下に示すように、両方のブランチがsameコンストラクターを呼び出す場合、これは条件付きの移動を使用してブランチを使用せずに非常に効率的に実装できます。 (つまり、2つのバージョンは実際に異なるコードを生成しますが、最初のバージョンの方が優れています。)
笑い声の場合、x86_64で_-O3
_を使用して、あなたのような_struct foo;
_と関数foo bar(char const * p) { return p; }
に対して、Clang 3.3から両方のバージョンを実行しました。
デフォルトのコンストラクター(std::string()
):
_ .cfi_offset r14, -16
mov R14, RSI
mov RBX, RDI
test R14, R14
je .LBB0_2
mov RDI, R14
call strlen
mov RDI, RBX
mov RSI, R14
mov RDX, RAX
call _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcm
jmp .LBB0_3
.LBB0_2:
xorps XMM0, XMM0
movups XMMWORD PTR [RBX], XMM0
mov QWORD PTR [RBX + 16], 0
.LBB0_3:
mov RAX, RBX
add RSP, 8
pop RBX
pop R14
ret
_
空の文字列コンストラクター(_""
_):
_ .cfi_offset r14, -16
mov R14, RDI
mov EBX, .L.str
test RSI, RSI
cmovne RBX, RSI
mov RDI, RBX
call strlen
mov RDI, R14
mov RSI, RBX
mov RDX, RAX
call _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcm
mov RAX, R14
add RSP, 8
pop RBX
pop R14
ret
.L.str:
.zero 1
.size .L.str, 1
_
私の場合、_""
_がbetterコードを生成するようにも見えます:両方のバージョンがstrlen
を呼び出しますが、空文字列バージョンは使用しませんジャンプ、条件付き移動のみ(同じコンストラクターが呼び出されるため、2つの異なる引数を使用)。もちろん、これは完全に無意味で、移植性がなく、譲渡できない観察結果ですが、コンパイラが必ずしもあなたが思っているほど多くの助けを必要としないことを示しています。最適なコードを記述してください。
まず最初に、あなたは正しいです http://www.cplusplus.com/reference/string/string/string/ から:
Sがnullポインターの場合、n == nposの場合、または[first、last)で指定された範囲が無効な場合、未定義の動作が発生します。
また、NULLポインタが何を意味するかによっても異なります。空の文字列と同じだと思います。
私が一番よく読んだので、私は最初のものに行きます。最初のソリューションと2番目のソリューションは同じです。文字列がconst
の場合、3つ目は機能しません。
cstr == NULL
が空のmStdString
を生成することに満足しているとすれば、おそらく最初のものが最も良いと思います。
他に何もない場合、mStdString
がconst
の場合、指定した3番目のオプションは機能しません。真ん中のオプションは、C++ 11での「セマンティクスの移動」から利益を得ますが、明らかに最適または合理的ではありません。
だから、私の投票は最初のオプションに行きます。
これは本当に回答とは限りませんが(特に質問を定式化した場合)、コメントとして収めるには長すぎて、コメントにorkしないコードが含まれています。私は全面的に反対票を投じることを期待しており、この投稿を削除する必要があります-しかし、私は何かを言わざるを得ませんでした。
なぜ初期化_char *
_がNULLになるのでしょうか-そうであれば、それを呼び出し元にプッシュして、その状況を理解することができませんでした-空の文字列を渡す、または_"unknown"
_または"(null)"
適切に。
つまり、次のようなものです。
_void MyClass::MyClass(const char *cstr)
{
assert(cstr != NULL); // or "throw cstr_must_not_be_null;" or some such.
mStdString = cstr;
}
_
(イニシャライザリストでこれを行うには、おそらくいくつかの巧妙な方法がありますが、それを正しく行う方法を理解するのに悩むことはできません)。
私は、「これは実際には存在しない」以外の方法で文字列パラメータへの入力としてNULLに熱心ではありません。そして、それが実際に複製しようとしているものである場合、boolean
で「doesn」と言う必要があります。 「存在しない」、または_std::string
_へのポインタ。文字列が存在しない場合はNULLにすることができます。