yohhoyの日記

技術的メモをしていきたい日記

range-based forと文字走査

C++11で導入された range-based for 構文を用いて、文字列を一文字づつ走査する方法。*1

文字列リテラル

文字列リテラルconst char配列型(→id:yohhoy:20150213)であるため、range-based for 構文による要素走査が可能。ただし文字列リテラル末尾に自動追加*2されるNUL終端文字('\0')も走査対象となることに留意。

#include <cstdio>

for (char c: "Hello") {  // const char[6]型
  std::printf("(%d)", c);
}
// (72)(101)(108)(108)(111)(0)

std::string型

C++標準ライブラリの文字列型(std::string)はbeginendメンバ関数を提供するため、range-based for 構文による要素走査が可能。下記コードのようにユーザ定義リテラル""sから構築された文字列オブジェクトはNUL終端文字を含まない*3。一方、実行時に文字列型の一時オブジェクト構築/破壊コストが生じる。

#include <cstdio>
#include <string>

using namespace std::string_literals;
for (char c: "Hello"s) {  // std::string型
  std::printf("(%d)", c);
}
// (72)(101)(108)(108)(111)

std::string_view型

C++1z(C++17)標準ライブラリに導入されるstd::string_view型もbeginendメンバ関数を提供するため、range-based for 構文による要素走査が可能。下記コードのようにユーザ定義リテラル""svから構築されたstring_viewオブジェクトはNUL終端文字を含まない*4。またstring_viewは文字列を “参照” するだけの軽量なデータ型のため、stringオブジェクト構築/破壊に比べ実行時コストが小さくなることを期待できる。

// C++1z(C++17)
#include <cstdio>
#include <string_view>

using namespace std::string_view_literals;
for (char c: "Hello"sv) {  // std::string_view型
  std::printf("(%d)", c);
}
// (72)(101)(108)(108)(111)

ノート:C++コンパイラの最適化処理により、一時オブジェクトのコンストラクタ/デストラクタ呼び出しは省略される可能性はある。GCC 7.0(HEAD)/-std=c++1z, -03オプションでアセンブリ出力を確認したところ、一時stringオブジェクトのコンストラクタ/デストラクタ呼び出しは削除されたが、グローバルdelete演算子(operator delete(void*))呼び出しだけは残っていた。一方のstring_view版では同演算子の呼び出しも含めて最適化されていた。*5

関連URL

*1:C++言語仕様では文字列リテラル文字集合は処理系定義(implementation defined)となっている。本記事においてはASCII文字集合(もしくはUTF-8等の互換文字集合)かつ一文字=1バイト(char)の幸せな世界を前提とする。

*2:C++14 2.14.5/p15:"After any necessary concatenation, in translation phase 7 (2.2), '\0' is appended to every string literal so that programs that scan a string can find its end."

*3:厳密には、ユーザ定義リテラル ""s に与えた文字列リテラル末尾に自動追加されるNUL終端文字のみが除外される。"a\0b\0"s のように文字列リテラル中にNUL終端文字を明示した場合、本文中コードの実行結果は (7)(0)(8)(0) となる。

*4:厳密には、ユーザ定義リテラル ""sv に与えた文字列リテラル末尾に自動追加されるNUL終端文字のみが除外される。

*5:string版:https://gist.github.com/yohhoy/76b9e0d7c30ccd81317fecb71c734847 string_view版:https://gist.github.com/yohhoy/ccade95cdbb345611bce8dcfc20847e7