1

次のコードでは、3 行目で作成された一時的な文字列が 4 行目の実行前に破棄されるため、"hello2" は表示されません。1 行目のように #define を使用するとこの問題を回避できますが、#define を使用せずにこの問題を回避する方法はありますか? (C++11 コードは問題ありません)

#include <iostream>
#include <string>

class C
{
public:
  C(const std::string& p_s) : s(p_s) {}
  const std::string& s;
};

int main()
{
  #define x1 C(std::string("hello1")) // Line 1
  std::cout << x1.s << std::endl; // Line 2

  const C& x2 = C(std::string("hello2")); // Line 3
  std::cout << x2.s << std::endl; // Line 4
}

説明:

Boost uBLAS は参照を保存すると信じていることに注意してください。これが、コピーを保存したくない理由です。値で保存することをお勧めする場合は、Boost uBLAS が間違っていて、値で保存してもパフォーマンスに影響しない理由を説明してください。

4

4 に答える 4

5

参照によって保存する式テンプレートは、通常、パフォーマンスのためにそうしますが、一時的なものとしてのみ使用されることに注意してください。

Boost.Protoのドキュメントから抜粋 (式テンプレートの作成に使用できます):

賢明な読者は、上記で定義されたオブジェクト y が、一時的な int へのダングリング参照を保持したままになっていることに気付くでしょう。Proto が扱う高性能アプリケーションでは、一時オブジェクトが範囲外になる前に式ツリーを構築して評価するのが一般的です。 . Proto は式ツリーをディープ コピーするためのユーティリティを提供するため、ダングリング リファレンスを気にせずに値型として渡すことができます。

最初の例では、これは次のことを意味します。

std::cout << C(std::string("hello2")).s << std::endl;

そうすれば、C一時的なものは一時的なものよりも長生きすることはありませんstd::stringsまたは、他の人が指摘したように、非参照メンバーを作成することもできます。

あなたがC++ 11について言及しているので、将来的には、移動セマンティクスを使用して高価なコピーを回避し、 std::reference_wrapper のようなラッパーを使用して、参照による格納のオプションを提供することで、式ツリーが値によって格納されることを期待しています。これは でうまく機能しautoます。

コードの考えられる C++11 バージョン:

class C
{
public:
    explicit
    C(std::string const& s_): s { s_ } {}

    explicit
    C(std::string&& s_): s { std::move(s_) } {}

    std::string const&
    get() const& // notice lvalue *this
    { return s; }

    std::string
    get() && // notice rvalue *this
    { return std::move(s); }

private:
    std::string s; // not const to enable moving
};

これは、次のようなコードがC("hello").get()メモリを一度だけ割り当てることを意味しますが、それでもうまく機能します

std::string clvalue("hello");
auto c = C(clvalue);
std::cout << c.get() << '\n'; // no problem here
于 2011-05-03T04:03:59.663 に答える
4

#define を使用せずにこの問題を回避する方法はありますか?

はい。

クラスを次のように定義します: (参照を保存しないでください)

class C
{
public:
  C(const std::string & p_s) : s(p_s) {}
  const std::string s; //store the copy!
};

コピーを保管してください!

デモ: http://www.ideone.com/GpSa2


あなたのコードの問題はstd::string("hello2")、一時的なものを作成し、のコンストラクターにいる限り生き続け、Cその後一時的なものは破棄されますが、オブジェクトはx2.sまだそれを指しています(死んだオブジェクト)。

于 2011-05-03T03:06:21.410 に答える
0

編集後:

参照による格納は危険であり、エラーが発生しやすい場合があります。変数参照が死ぬまでスコープ外に出ないことが 100% 確実な場合にのみ実行してください。

C++stringは非常に最適化されています。文字列値を変更するまで、すべてが同じ文字列のみを参照します。それをテストするにはoperator new (size_t)、デバッグ ステートメントをオーバーロードして配置します。同じ文字列の複数のコピーの場合、メモリ割り当てが 1 回だけ行われることがわかります。

クラス定義は参照によって保存するのではなく、値によって次のように保存する必要があります。

class C {
  const std::string s;  // s is NOT a reference now
};

この質問が一般的な意味である場合 (文字列に固有のものではない)、最善の方法は動的割り当てを使用することです。

class C {
  MyClass *p;
  C() : p (new MyClass()) {}  // just an example, can be allocated from outside also
 ~C() { delete p; }
};
于 2011-05-03T03:08:17.587 に答える
0

BLAS を見なくても、式テンプレートは通常、存在することすら知らないはずの一時オブジェクトを多用します。Boost がこのような参照を自分の内部に保存している場合、ここで見たのと同じ問題が発生します。ただし、これらの一時オブジェクトが一時的なままであり、ユーザーが後でそれらを保存しない限り、それらが参照する一時オブジェクトは一時オブジェクトが存在する限り存続するため、すべて問題ありません。秘訣は、中間オブジェクトがユーザーが格納する最終オブジェクトに変わるときにディープ コピーを実行することです。ここでは、この最後の手順をスキップしました。

要するに、これは危険な動きであり、ライブラリのユーザーが愚かなことをしない限り、完全に安全です。明確な必要性がなく、その結果を十分に認識していない限り、それを使用することはお勧めしません. それでも、より良い代替手段があるかもしれません.私は式テンプレートを真剣に扱ったことはありません.

余談ですが、この C++0x にタグを付けたのでauto x = a + b;、コードのユーザーが最適化を危険にするために行うことができる「ばかげた」ことの 1 つになるようです。

于 2011-05-03T03:34:01.527 に答える