コンテンツにスキップ

最終更新:2026-10-12

C++ スマートポインタを関数引数にする

C++那些事:スマートポインタはいつ関数引数にすべきか

スマートポインタを引数に使う関数もあれば、生ポインタを使う関数もあり、どちらを使うべきなのでしょうか?使い分けの原則は何でしょうか?

この節ではabseilのtipsを使って解説します。

次のコードには何の問題があるでしょうか?

bool CanYouPetTheDog(const std::shared_ptr<Dog>& dog,
                     absl::Duration min_delay) {
  return dog->GetLastPetTime() + min_delay < absl::Now();
}

CanYouPetTheDog関数はその dog 引数の所有権に影響を与えませんが、そのシグネチャは std::shared_ptr に格納することを要求しています。これにより、特定の所有権モデルへの不要な依存が生み出されてしまい、関数自体はそのような依存を必要としません。この依存により、呼び出し側は std::unique_ptr を使ったり、オブジェクトをスタック上に構築したりするといった、他のモデルを使うことができなくなります。

ポインタ/参照を使う

所有権が影響を受けないときは**参照またはポインタ**を使います。参照/ポインタを使うことで、特定の所有権モデルへの依存をなくし、関数が Dog 型の任意のオブジェクトに対して動作できるようになります。

bool CanYouPetTheDog(const Dog& dog, absl::Duration min_delay) {
  return dog.GetLastPetTime() + min_delay < absl::Now();
}

上のように定義すれば、呼び出し側の所有権モデルがどのようなものであっても、この関数を呼び出すことができます:

Dog stack_dog;
if (CanYouPetTheDog(stack_dog, delay)) { ... }

auto heap_dog = std::make_unique<Dog>();
if (CanYouPetTheDog(*heap_dog, delay)) { ... }

CustomPetPtr<Dog> custom_dog = CreateDog();
if (CanYouPetTheDog(*custom_dog, delay)) { ... }

関数が渡された値を変更する場合は、変更可能な参照または生ポインタを渡し、上述と同じ使い方をします。

スマートポインタを使う

関数が所有権を変更する場合はスマートポインタを使います。次のコードは、異なるスマートポインタ引数のためにいくつかのオーバーロードを提供しています。最初のオーバーロードは渡されたオブジェクトの所有権を引き取ることを前提とし、2番目のオーバーロードは渡されたオブジェクトに対して共有参照を1つ追加します。この2つの操作は、呼び出し側が Dog の所有権をどのように扱うかによって決まります。スタック上にある Dog の所有権は引き取ることができません。スタックから所有権を取り出すことはできないからです。

class Human {
 public:
  ...
  void Adopt(std::unique_ptr<Dog> dog) {
    pets_.push_back(std::move(dog));
  }
  void Adopt(std::shared_ptr<Cat> cat) {
    pets_.push_back(std::move(cat));
  }

 private:
  std::vector<std::shared_ptr<Pet>> pets_;
  ...
};

まとめ

所有権が移転も変更もされないのであれば、スマートポインタを関数引数にするのは避けるべきです。

https://abseil.io/tips/188

関連トピック

コメント