私は最近、しばらく不在だったのですが、再び C++11 を試していました。インターネットで多くの記事を読んだ後、ファクトリ関数からラージ オブジェクトを返す最も効率的な方法 (基本的には、データベース)。
私はunique_ptrのファンになりましたが、いくつかの記事で、新しいムーブコンストラクターのおかげで、大きなベクトルを値で返すことが完全に可能になり、これらの新しいセマンティクスにより、1つのポインターをコピーするのと同じくらい高速になるはずだと読みました.
これを試すために、さまざまなコンストラクターで出力を行う小さなテスト プログラムを作成しました。
#include <iostream>
#include <memory>
using namespace std;
class C {
public:
C( string n ) : _name{n} { cout << "Constructing a C named '" << _name << "'\n"; };
C() : _name( "EMPTY" ) { cout << "Default-constructing a C named '" << _name << "'\n"; } ; // default-ctor
C( const C& c ) : _name{c._name} {
_name += " [copied]";
cout << "Copy-constructing a C named '" << _name << "'\n";
};
C( C&& c )
: _name{c._name} {
_name += " [moved]";
cout << "Move-constructing a C named '" << _name << "'\n";
};
~C() { cout << "Destructing a C named '" << _name << "'\n"; };
string getName() { return _name; };
private:
string _name;
};
およびテスト済み
C fooVal() {
cout << "In fooVal\n";
string str = "value return";
C c(str);
return c;
}
C& fooRef() {
cout << "In fooRef\n";
string str = "reference return";
C* pC = new C( str );
return *pC;
}
C* fooPtr() {
cout << "In fooPtr\n";
string str = "classical pointer return";
C* pC = new C( str );
return pC;
}
unique_ptr<C> fooUPtr() {
cout << "In fooUPtr\n";
string str = "unique_ptr return";
return unique_ptr<C>(new C(str));
}
shared_ptr<C> fooSPtr() {
cout << "In fooSPtr\n";
string str = "shared_ptr return";
return shared_ptr<C>(new C(str));
}
// IMPORTANT: THIS NEEDS TO BE COMPILED WITH FLAG -fno-elide-constructors
int main(int argc, const char * argv[])
{
C cv(fooVal());
cout << "cv constructed\n";
C& cr = fooRef();
cout << "cr constructed\n";
C* pC = fooPtr();
cout << "*pC constructed\n";
unique_ptr<C> upC = fooUPtr();
cout << "*upC constructed\n";
shared_ptr<C> spC = fooSPtr();
cout << "*spC constructed\n";
cout << "Alive: " << cv.getName() << ", " << cr.getName() << ", " << pC->getName() << ", " << upC->getName() << ".\n";
}
これをそのままコンパイルすると、コンパイラはさまざまなコンストラクター呼び出しを最適化 (「省略」) し、出力を取得します。
In fooVal
Constructing a C named 'value return'
cv constructed
In fooRef
Constructing a C named 'reference return'
cr constructed
In fooPtr
Constructing a C named 'classical pointer return'
*pC constructed
In fooUPtr
Constructing a C named 'unique_ptr return'
*upC constructed
In fooSPtr
Constructing a C named 'shared_ptr return'
*spC constructed
Alive: value return, reference return, classical pointer return, unique_ptr return.
Destructing a C named 'shared_ptr return'
Destructing a C named 'unique_ptr return'
Destructing a C named 'value return'
わかりましたが、コピーがどれだけ最適化されているかがわかります。「指定された」動作が何であるかを確認するために、フラグ -fno-elide-constructors を使用してこれをコンパイルしました (Apple LLVM バージョン 4.2 (clang-425.0.28) を使用しています)。しかし、次の出力が得られます。
In fooVal
Constructing a C named 'value return'
Destructing a C named 'value return'
Move-constructing a C named ' [moved]'
Destructing a C named ''
cv constructed
In fooRef
Constructing a C named 'reference return'
cr constructed
In fooPtr
Constructing a C named 'classical pointer return'
*pC constructed
In fooUPtr
Constructing a C named 'unique_ptr return'
*upC constructed
In fooSPtr
Constructing a C named 'shared_ptr return'
*spC constructed
Alive: [moved], reference return, classical pointer return, unique_ptr return.
Destructing a C named 'shared_ptr return'
Destructing a C named 'unique_ptr return'
Destructing a C named ' [moved]'
したがって、明らかに、値が返されるオブジェクトで怪しいことが起こっています。明らかに、これは単なる小さな問題ではありません。なぜなら、-fno-elide-constructors はセマンティクスを変更せず、関与するコンストラクターの量だけを変更すると予想していたからです。
したがって、私は尋ねます:
- 何が起こっている?値オブジェクトが文字列パラメータを「失う」のはなぜですか? そしてどこに?
- 値の戻り値に問題があるように見えますが、他の戻り値は問題なく機能します。では、なぜ人々は最近、価値を返し、「残りはシステムが処理する」ことを推奨しているのでしょうか?
- 大きなオブジェクトを返す良い方法は何ですか?
- 見えないところでどこか間違っていますか?
ありがとう!