最終更新日時(UTC):
が更新

履歴 編集

class template
<type_traits>

std::has_virtual_destructor(C++11)

namespace std {
  template <class T>
  struct has_virtual_destructor;

  template <class T>
  inline constexpr bool has_virtual_destructor_v
    = has_virtual_destructor<T>::value;          // C++17
}

概要

Tが仮想デストラクタを持っているか調べる

要件

Tが非共用体のクラスである場合、その型は完全型でなければならない。

効果

has_virtual_destructorは、型Tが仮想デストラクタを持っていればtrue_typeから派生し、そうでなければfalse_typeから派生する。

例1

#include <type_traits>

struct A {
  ~A() = default;
};

struct B {
  virtual ~B() = default;
};

static_assert(std::has_virtual_destructor<A>::value == false, "A hasn't virtual destructor");
static_assert(std::has_virtual_destructor<B>::value == true,  "B has virtual destructor");

static_assert(std::is_same<std::has_virtual_destructor<A>::type, std::false_type>::value, "B hasn't virtual destructor");
static_assert(std::is_same<std::has_virtual_destructor<B>::type, std::true_type>::value, "B has virtual destructor");

static_assert(std::has_virtual_destructor<int>::value == false, "int hasn't virtual destructor");
static_assert(std::has_virtual_destructor<int[]>::value == false, "array hasn't virtual destructor");
static_assert(std::has_virtual_destructor<void>::value == false, "void hasn't virtual destructor");

int main() {}

出力

例2

#include <type_traits>
#include <cstddef>
#include <iostream>
#include <memory>

struct nonvirtual_base {
  ~nonvirtual_base() { std::cout << "  nonvirtual_base::~nonvirtual_base()" << std::endl; }
};

struct virtual_base {
  virtual ~virtual_base() { std::cout << "  virtual_base::~virtual_base()" << std::endl; }
};

template<typename Base>
struct unsafe_derived : Base {  // 任意の型を基底クラスに取れてしまう
  ~unsafe_derived() { std::cout << "  unsafe_derived::~unsafe_derived()" << std::endl; }
};

template<typename Base, typename std::enable_if<std::has_virtual_destructor<Base>::value, std::nullptr_t>::type = nullptr>
struct safe_derived : Base {  // 仮想デストラクタを持つクラスでないと基底クラスに取れない
  ~safe_derived() { std::cout << "  safe_derived::~safe_derived()" << std::endl; }
};

int main() {
  {
    std::cout << "unsafe_derived<virtual_base> :" << std::endl;
    std::unique_ptr<virtual_base> p{new unsafe_derived<virtual_base>};
    // virtual_baseは仮想デストラクタを持つので ~unsafe_derived() と ~virtual_base() が呼ばれる
  }
  std::cout << "---" << std::endl;
  {
    std::cout << "unsafe_derived<nonvirtual_base> :" << std::endl;
    std::unique_ptr<nonvirtual_base> p{new unsafe_derived<nonvirtual_base>};
    // nonvirtual_baseは仮想デストラクタを持たないので ~nonvirtual_base() しか呼ばれない
    //   (unsafe_derived のメンバのデストラクトが行われなくなるのでリソースリークを起こす)
  }
  std::cout << "---" << std::endl;
  {
    std::cout << "safe_derived<virtual_base> :" << std::endl;
    std::unique_ptr<virtual_base> p{new safe_derived<virtual_base>};
    // virtual_baseは仮想デストラクタを持つので ~safe_derived() と ~virtual_base() が呼ばれる
  }
  std::cout << "---" << std::endl;
  {
    std::cout << "safe_derived<nonvirtual_base> :" << std::endl;
    // std::unique_ptr<nonvirtual_base> p{new safe_derived<nonvirtual_base>};
    // nonvirtual_baseは仮想デストラクタを持たないので safe_derived のテンプレートパラメータに指定できないため,コメントアウトを外すとコンパイルエラーになる
    //   (std::has_virtual_destructor を用いることでリソースリークが起こるようなコードをコンパイルできなくすることができる)
  }
  std::cout << "---" << std::endl;
}

出力

unsafe_derived<virtual_base> :
  unsafe_derived::~unsafe_derived()
  virtual_base::~virtual_base()
---
unsafe_derived<nonvirtual_base> :
  nonvirtual_base::~nonvirtual_base()
---
safe_derived<virtual_base> :
  safe_derived::~safe_derived()
  virtual_base::~virtual_base()
---
safe_derived<nonvirtual_base> :
---

バージョン

言語

  • C++11

処理系

参照