boost::string_ref に c_str() がない理由

string_ref について

Boost 1.53.0 で追加された String_Ref は、文字列の所有権を持たずに先頭のポインタとサイズだけを持つクラスです。

const string& の引数を受け取る関数に const char* を渡すと、不要な string オブジェクトが作成されてしまいますが、string_ref を使うことで string と const char* の両方に対応しつつ、余計なアロケーションを防ぐことができます。

サンプルは Faith and Brave - C++で遊ぼう boost::string_ref を参照してください。

string_ref は length() や begin/end(), operator[] など、string とよく似たインターフェイスを持っていますが、c_str() メンバ関数はありません。どうしてでしょう?

c_str() がない理由を考える

string_ref の substr() は、効率のために新しい文字列を作成せず、元のポインタの位置とサイズを変更しただけの新しい string_ref を返します。

そのため、substr() された string_ref から C 言語形式の文字列のポインタを取得した場合、意図する終端でそのポインタが null-terminate している保証がありません。

次のようなコードの動作を見てみましょう。

# include <iostream>
# include <boost/utility/string_ref.hpp>

void Print( const boost::string_ref& str )
{
	std::cout << str << '\n'
			  << str.data() << '\n'
			  << &str[0] << '\n';
}

int main()
{
	const boost::string_ref str = "The quick brown fox";

	Print(str.substr(4,5));
}
quick
quick brown fox
quick brown fox

文字列のポインタと長さを受け取るコンストラクタでも同じことが起こります。

// ...

int main()
{
	const boost::string_ref str("The quick brown fox",3);

	Print(str);
}
The
The quick brown fox
The quick brown fox
  • c_str() は string_ref 自身が表現する文字列と一致する、null-terminated な文字列へのポインタを返す
  • string_ref はオリジナルの文字列データを変更しない
  • string_ref はアロケーションを行わない

という、ユーザーが期待する条件が共存できないため c_str() メンバ関数は実装されなかったのでしょう。

上記のコードで示したように、const char* を受け取る関数に string_ref を使うときは注意が必要です。