私は最近、既存のC++アプリケーションコードの多くをC++ 11に移植し始め、新しいスマートポインターstd :: unique_ptrおよびstd :: shared_ptr、カスタム削除プログラムについて具体的な質問があります。ラムダロガーを追加して、削除が呼び出される場所を確認したいのですが、配列の特殊化バージョンをコンパイルできません。アドバイスをいただければ幸いです。
unique_ptrforVC++ 10またはGCC 4.5.2 +。削除者がラムダで呼び出されたときにログメッセージを出力したいと思います。主に、範囲外に出ていると思われるすべてのポインターがそうしていることを確認するためです。これはスペシャライゼーションのアレイバージョンで可能ですか?非配列バージョンで動作するようにできます。また、2番目の引数として外部構造体「MyArrayDeleter」を渡すと、配列特殊化で動作するようになります。もう1つ、ラムダシグネチャにそれを理解させることができると思ったので、醜いstd :: functionを削除することは可能でしょうか。
struct MySimpleDeleter {
void operator()(int* ptr) const {
printf("Deleting int pointer!\n");
delete ptr;
}
};
struct MyArrayDeleter {
void operator()(int* ptr) const {
printf("Deleting Array[]!\n");
delete [] ptr;
}
};
{
// example 1 - calls MySimpleDeleter where delete simple pointer is called
std::unique_ptr<int, MySimpleDeleter> ptr1(new int(5));
// example 2 - correctly calls MyArrayDeleter where delete[] is called
std::unique_ptr<int[], MyArrayDeleter> ptr2(new int[5]);
// example 3 - this works (but default_delete<int[]> would have been passed
// even if I did not specialize it as it is the default second arg
// I only show it here to highlight the problem I am trying to solve
std::unique_ptr<int[], std::default_delete<int[]>> ptr2(new int[100]);
// example 3 - this lambda is called correctly - I want to do this for arrays
std::unique_ptr<int, std::function<void (int *)>> ptr3(
new int(3), [&](int *ptr){
delete ptr; std::cout << "delete int* called" << std::endl;
});
// example 4 - I cannot get the following like to compile
// PLEASE HELP HERE - I cannot get this to compile
std::unique_ptr<int[], std::function<void (int *)>> ptr4(
new int[4], [&](int *ptr){
delete []ptr; std::cout << "delete [] called" << std::endl;
});
}
The compiler error is as follows:
The error from the compiler (which complains about the new int[4] for ptr4 below is:
'std::unique_ptr<_Ty,_Dx>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty,_Dx>'
1> with
1> [
1> _Ty=int [],
1> _Dx=std::tr1::function<void (int *)>
1> ]
1> c:\program files (x86)\Microsoft visual studio 10.0\vc\include\memory(2513) : see declaration of 'std::unique_ptr<_Ty,_Dx>::unique_ptr'
1> with
1> [
1> _Ty=int [],
1> _Dx=std::tr1::function<void (int *)>
1> ]
何について:
auto deleter=[&](int* ptr){...};
std::unique_ptr<int[], decltype(deleter)> ptr4(new int[4], deleter);
まず、VC2010 SP1、Mingw g ++ 4.7.1を使用します。
配列newの場合、unique_ptrはすでにクリーンな方法でサポートしています。
struct X
{
X() { puts("ctor"); }
~X() { puts("dtor"); }
};
unique_ptr<X[]> xp(new X[3]);
出力は次のとおりです。
ctor
ctor
ctor
dtor
dtor
dtor
カスタマイズされた削除プログラムの場合、残念ながら、VC2010とg ++の間で一貫性がありません。
VC2010:
unique_ptr<FILE, function<void (FILE*)> > fp(fopen("tmp.txt", "w"), [](FILE *fp){
puts("close file now");
fclose(fp);
});
g ++:
unique_ptr<FILE, void (*)(FILE*) > fp(fopen("tmp.txt", "w"), [](FILE *fp){
puts("close file now");
fclose(fp);
});
インラインラムダはクールですが読みやすさを損なうため、Managuによる方法は非常に優れています。また、取得前にリソースを解放することも強調しています(RAII)。
ここでは、リソースの取得と解放を分離する宣言的な方法を提案します(Scope Guard、VC2010とg ++ 4.7.1の両方で機能します)。
template<typename T>
struct ScopeGuard
{
T deleter_;
ScopeGuard( T deleter) : deleter_(deleter) {}
~ScopeGuard() { deleter_() ; }
};
#define UNI_NAME(name, line) name ## line
#define ON_OUT_OF_SCOPE_2(lambda_body, line) auto UNI_NAME(deleter_lambda_, line) = [&]() { lambda_body; } ; \
ScopeGuard<decltype(UNI_NAME(deleter_lambda_, line))> \
UNI_NAME(scope_guard_, line) ( UNI_NAME(deleter_lambda_, line ));
#define ON_OUT_OF_SCOPE(lambda_body) ON_OUT_OF_SCOPE_2(lambda_body, __LINE__)
FILE * fp = fopen("tmp.txt", "w");
ON_OUT_OF_SCOPE( { puts("close file now"); fclose(fp); } );
ポイントは、古い明確な方法でリソースを取得し、リソース取得行の直後にリソースを解放するステートメントを宣言できることです。
欠点は、単一のオブジェクトをその削除機能と一緒に転送できないことです。
FILE *の場合、shared_ptrを同じ目的の代替ポインターとして使用できます(多分少し重いですが、VC2010とg ++の両方でうまく機能します)
shared_ptr fp2(fopen( "tmp.txt"、 "w")、[](FILE * fp){fclose(fp); puts( "close file");});