web-dev-qa-db-ja.com

関数にローカルなstd :: stringを返す最良の方法

C++では、関数から関数ローカルstd :: string変数を返す最良の方法は何ですか?

std::string MyFunc()
{
    std::string mystring("test");
    return mystring;

}

std::string ret = MyFunc(); // ret has no value because mystring has already gone out of scope...???
50
Tony The Lion

いいえ、そうではありません。 mystringがスコープ外に出て破棄された場合でも、retは関数MyFuncが値で返すためmystringのコピーを持っています。

70
Chubsdad

コードが次のような場合、問題が発生します。

std::string& MyFunc()
{
    std::string mystring("test");
    return mystring;
}

だから、あなたが書いた方法は大丈夫です。 1つのアドバイス-このような文字列を作成できる場合、つまり-1行で実行できる場合は、次のようにすることをお勧めします。

std::string MyFunc()
{
    return "test";
}

または、より複雑な場合、たとえば:

std::string MyFunct( const std::string& s1, 
                     const std::string& s2, 
                     const char* szOtherString )
{
    return std::string( "test1" ) + s1 + std::string( szOtherString ) + s2;
}

これにより、コンパイラにhintが与えられ、より最適化が行われるため、文字列(RVO)のコピーが1つ少なくなります。

21
Kiril Kirov

前述のように、std :: stringがコピーされます。したがって、元のローカル変数が範囲外になった場合でも、呼び出し元はstd :: stringのコピーを取得します。

[〜#〜] rvo [〜#〜] を読むと混乱を完全に解消できると思います。この場合、正確にはNRVO(RVOという名前)と呼ばれますが、その精神は同じです。

ボーナスリーディング:RVOを使用することの問題は、RVOが世界で最も柔軟なものではないことです。 C++ 0xの大きな話題の1つは、 右辺値参照 で、この問題を解決しようとしています。

6
kizzx2

試しましたか?文字列は、返されるときにコピーされます。まあそれは公式の行です、実際にはコピーはおそらく最適化されて離れていますが、どちらの方法でも安全に使用できます。

5

さて、retはMyFunc()の後にmystringの値を持ちます。値で結果を返す場合、ローカルオブジェクトをコピーして一時オブジェクトが作成されます。

私に関しては、これらのセクションのトピックに関する興味深い詳細が C++ FAQ Lite にあります。

3
Paul E.

ユースケースに依存します。インスタンスが文字列の責任を保持する必要がある場合、const参照によってスティングが返されます。問題は、返すオブジェクトがない場合の対処方法です。ポインターを使用すると、0を使用して無効なオブジェクトを通知できます。このような「null-object」は、参照(コードスニペットのNullStringなど)でも使用できます。無効な戻り値を通知するより良い方法は、例外をスローすることです。

別の使用例は、文字列の責任が呼び出し元に転送される場合です。この場合、auto_ptrを使用する必要があります。以下のコードは、このすべてのユースケースを示しています。

#include <string>
#include <memory> //auto_ptr
#include <iostream>
using std::string;
using std::auto_ptr;
using std::cout;
using std::endl;

static const string NullString("NullString\0");


///// Use-Case: GETTER //////////////////
//assume, string should be found in a list
//  and returned by const reference

//Variant 1: Pseudo null object
const string & getString( bool exists ) {
  //string found in list
  if( exists ) {
    static const string str("String from list");
    return str;
  }
  //string is NOT found in list
  return NullString;
}

//Variant 2: exception
const string & getStringEx( bool available ) {
  //string found in list
  if( available ) {
    static const string str("String from list");
    return str;
  }

  throw 0; //no valid value to return
}

///// Use-Case: CREATER /////////////////
auto_ptr<string> createString( bool ok )
{
  if( ok ){
    return auto_ptr<string>(new string("A piece of big text"));
  }else{
    return auto_ptr<string>();
  }
}

int main(){
  bool ok=true, fail=false;
  string str;
  str = getString( ok );
  cout << str << ", IsNull:"<<( str == NullString )<<endl;
  str = getString( fail );
  cout << str << ", IsNull:"<<( str == NullString )<<endl;

  try{
    str = getStringEx( ok );
    cout << str <<endl;
    str = getStringEx( fail );
    cout << str <<endl; //line won't be reached because of ex.
  }
  catch (...)
  {
    cout << "EX: no valid value to return available\n";
  }

  auto_ptr<string> ptext = createString( ok );
  if ( ptext.get() ){
    cout << *ptext << endl;
  } else {
      cout << " Error, no text available"<<endl;   
  }

  ptext = createString( fail );
  if ( ptext.get() ){
    cout << *ptext << endl;
  } else {
      cout << " Error, no text available"<<endl;   
  }

return 0;
}

よろしく、バレンティン・ハイニッツ

2