HOWTO · C++
C++ で関数から文字列を返す方法
この記事では、C++ で関数から文字列を返す方法を示します。
このページの内容
通常のC++関数では、std::stringを値で返します。呼び出し側は独立した文字列を受け取り、可能な場合はC++の戻り値最適化が適用されます。参照を使うのは、寿命が別の場所で管理されている既存のオブジェクトを返す場合に限ります。ポインターやC形式文字列は、インターフェイスで必要とされる場合に使います。以下の例はC++11以降を対象としています。
文字列を値で返す
関数で文字列を作成または変換する場合、値で返すのが最も明快な既定の方法です。呼び出し側は独立した文字列値を受け取り、ほかの値とは別に保存したり変更したりできます。名前付きのローカル変数を返す方法も通常は効率的です。コンパイラーは名前付き戻り値最適化により、呼び出し側の格納先に結果を直接構築できます。この最適化が適用されない場合は、std::stringをムーブできます。どちらの場合も、return文にstd::moveを付ける必要はありません。
次の例を実行してください。コードと表示されるラベルや出力は英語のままです。
#include <iostream>
#include <string>
std::string makeGreeting() {
std::string result = "Hello, C++!";
return result;
}
int main() {
std::string greeting = makeGreeting();
std::cout << greeting << '\n';
}
関数の戻り値の型はstd::stringで、return result;はローカル変数の値を返します。ローカル変数は関数の終了時に破棄されますが、返された文字列は呼び出し側で引き続き使えます。値で返す方法は、動的に確保した文字列の所有権を別途管理するより、通常は簡潔で安全です。
既存の文字列への参照を返す
参照を返すと、すでに存在するオブジェクトを参照します。新しい文字列が作られるわけではありません。寿命の長いオブジェクトが所有する文字列を関数で選択したり公開したりする場合に便利です。呼び出し側が見るのは同じオブジェクトなので、非const参照を通じた変更は元のオブジェクトにも反映されます。読み取りだけを許可する場合は、const std::string&を使います。
返された参照を使う間、参照先のオブジェクトが存続していなければなりません。ローカル変数への参照を返してはいけません。関数の終了時にその変数が破棄され、呼び出し側にはダングリング参照が残ります。参照引数を安全に返せるのは、次の例のように、呼び出し側が引数を使い終わるまでその寿命を保つ場合に限られます。コードと表示されるラベルや出力は英語のままです。
#include <iostream>
#include <string>
const std::string& sameString(const std::string& value) {
return value;
}
int main() {
std::string original = "Existing string";
const std::string& alias = sameString(original);
std::cout << alias << '\n';
}
文字列へのポインターを返す
ポインターを返す方法は、アクセス対象が任意であることを表したり、既存のインターフェイスに合わせたりできますが、寿命と所有権も契約の一部になります。既存の文字列へのアドレスを返すのは、その文字列がポインターの使用期間中ずっと存続する場合に限るのがよいでしょう。誰がオブジェクトを所有するのか、ポインターがnullになり得るのかを明記してください。
文字列を返すだけの目的でstd::stringをnewで確保してはいけません。そうすると呼び出し側は削除を忘れないようにする必要があり、早期リターンやエラーへの対処も難しくなります。新しく作った文字列は値で返します。次の例では既存のオブジェクトを指し、ポインターの使用中はそのオブジェクトが存続します。コードと表示されるラベルや出力は英語のままです。
#include <iostream>
#include <string>
const std::string* findGreeting(const std::string& value) {
return &value;
}
int main() {
std::string greeting = "Hello through a pointer";
const std::string* result = findGreeting(greeting);
if (result != nullptr) {
std::cout << *result << '\n';
}
}
この関数はvalueの所有権を取得しません。参照先の文字列が破棄された後に、呼び出し側が返されたポインターを使い続けてはいけません。
効果がある場合に限ってstd::moveを使う
std::moveはムーブ操作を可能にするキャストであり、それ自体がデータを移動するわけではありません。return文でreturn std::move(local);と書くのは通常不要です。名前付き戻り値最適化を妨げ、直接構築の代わりにムーブが行われる可能性があります。ローカル変数の結果にはreturn local;を使い、適用可能な最適化は言語とコンパイラーに任せましょう。
明示的なムーブは、自動的なムーブの対象にならないオブジェクトから値を移す場合に役立ちます。たとえば、寿命の長いオブジェクトからメンバーを取り出す場合です。ムーブすると元のオブジェクトの状態が変わるため、以前の状態が不要な場合に限って使います。戻り値を速く見せるためだけにstd::moveを追加しないでください。
char*でC形式文字列を返す
C形式文字列はnull終端文字列であり、std::stringではありません。APIが文字ポインターを要求し、結果が固定テキストなら、文字列リテラルを指すconst char*を返します。文字列リテラルの記憶域期間は静的なので、ポインターはプログラムの実行中有効です。ここでconst char*は、変更できない文字を指すポインターです。ポインター自体はconstではないため、別のアドレスを代入できます。これは、変更可能な文字を指すconstポインターであるchar* constとは異なります。文字列リテラルそのものを変更してはいけません。
例のコードと表示されるラベルや出力は英語のままです。
#include <iostream>
const char* greeting() {
return "Hello from C-style text";
}
int main() {
std::cout << greeting() << '\n';
}
関数の終了時に消滅するローカル文字配列へのポインターや、ローカルなstd::stringの内部を指すポインターを返してはいけません。c_str()が返すポインターの有効性はstd::stringオブジェクトの寿命に依存し、その文字列を変更する操作によって無効になる場合があります。呼び出し側が独立して所有し、変更できる結果を必要とするなら、std::stringを返してください。