🍣

ダブルポインタだと一時的なポインタを渡せない場合がある

2025/01/25に公開

たまに以下のような引数がポインタのポインタになっているAPIを見かけます。

void myFunction(MyStruct** myStruct);

引数がダブルポインタになっている関数って、新しい C++ を意識してコードを書いていると、以下のようなことができないから、非常に使いづらいですね。

MyStruct myStruct;
myFunction(&(&myStruct)); //エラー
std::unique_ptr<MyStruct> myStruct(std::make_unique<MyStruct>());
myFunction(&(myStruct.get())); //エラー

上記のどちらの場合もポインタがアドレスがない右辺値になっていて、&演算子でアドレスを取得しようとするとエラーになります。
なので、どうしても以下のように、生ポインター型の変数を作らないと駄目です。

MyStruct myStruct;
MyStruct* myStructPtr = &myStruct;
myFunction(&myStructPtr); //成功
std::unique_ptr<MyStruct> myStruct(std::make_unique<MyStruct>());
MyStruct* myStructPtr = myStruct.get();
myFunction(&myStructPtr); //成功

C言語では以下のように一行で名前のないローカルの変数を作成できたようですが、C++ にはそういう機能はないですね。

MyStruct myStruct;
myFunction(&(MyStruct*){&myStruct});

モダン C++ では、ダブルポインタを使うユースケースは殆どないため、今後も追加されることはないと思います。

C++ 勉強し始めたばかりの頃は、クラスや構造体を全部newで作っていた時期がありました。
ダブルポインタを使う API はスマートポインタもRVO (Return Value Optimization)もなかった時代の名残だと思います。

そもそもmyFunctionMyStructのインスタンスを渡して、中でその中身を変えたい場合は、以下のように引数を参照型か普通のポインタにすると思います。

void myFunction(MyStruct& myStruct);
void myFunction(MyStruct* myStruct);

ダブルポインタにしているってことは、ポインタ先のMyStructのインスタンスではなく、ポインタ自体を変えたいということになります。
MyStructの配列の配列という可能性もありますが、ダブルポインタが使われる場合って、十中八九その中でnewmallocMyStructのインスタンスを作成して返しています。

そういうことがやりたい場合は、モダン C++ では、以下のようにMyStructを直接返すと思います。
Return Value Optimization (RVO)によって、ほとんどの場合にオブジェクトのコピーが省略され、効率的なコードが生成されます。
RVOはC++の標準規格に追加されたのはC++17ですが、それ以前から多くのコンパイラで実装されている機能です)

MyStruct myFunction();

どうしてもヒープで作成しないといけない場合は、以下のようにstd::unique_ptrに入れて返さないと、そのメモリがちゃんと開放されているかが確認しづらくなり、メモリーリークの恐れがあります。

std::unique_ptr<MyStruct> myFunction();
void myFunction(std::unique_ptr<MyStruct>& myStruct);

|cpp記事一覧へのリンク|

Discussion