Skip to content

std::is_default_destructible и друзья #484

@pavelkryukov

Description

@pavelkryukov

Предлагается ввести следующие type traits:

namespace std {
    template<class T> struct is_default_destructible;
    template<class T> struct is_default_copy_constructible;
    template<class T> struct is_default_move_constructible;
    template<class T> struct is_default_copy_assignable;
    template<class T> struct is_default_move_assignable;
} // namespace std

Эти структуры предоставляют ::value == true тогда и только тогда, когда соответствующая функция определяется компилятором:

  • явно посредством синтаксиса = default;
  • неявно при отсутствии какой-либо декларации.

is_default_XXX — более строгое условие, чем is_XXX, но менее строгое, чем is_trivially_XXX.

Примеры

struct A {
    int a;
};

static_assert(std::is_copy_constructible<A>::value);
static_assert(std::is_default_copy_constructible<A>::value);
static_assert(std::is_trivially_copy_constructible<A>::value);

struct B {
    std::string b;
};

static_assert(std::is_copy_constructible<B>::value);
static_assert(std::is_default_copy_constructible<B>::value);
static_assert(std::is_trivially_copy_constructible<B>::value == false);

struct C {
    std::string c;
    C(const C& rhs) : c(rhs.c + "_copy") { }
};

static_assert(std::is_copy_constructible<C>::value);
static_assert(std::is_default_copy_constructible<C>::value == false);
static_assert(std::is_trivially_copy_constructible<C>::value == false);

struct D {
    std::unique_ptr<std::string> d;
};

static_assert(std::is_copy_constructible<D>::value == false);
static_assert(std::is_default_copy_constructible<D>::value == false);
static_assert(std::is_trivially_copy_constructible<D>::value == false);

Мотивация

Наличие свойства is_default_... говорит, что объект этого типа может быть безопасно создан/скопирован/перемещён/уничтожен применением операции к каждому из полей, при этом нет ограничений на каждое поле.
Такие type traits обобщают работу с агрегатами, в частности, их преобразование в кортежи и обратно через Boost.PFR, и преобразование в менее стандартные структуры данных (например, SoA).

На текущий момент С++ имеет type trait is_aggregate, имеющий похожий функционал.
Однако, соответствующим ему типам позволено иметь явно определённые операторы присваивания и деструкторы:

struct Aggregate {
    std::string a;
    std::string b;
    std::string c;
    Aggregate& operator=(const Aggregate& rhs) { std::tie(a, b, c) = std::tie(c, a, b); return *this; }
    Aggregate& operator=(Aggregate&& rhs)      { std::tie(a, b, c) = std::tie(b, c, a); return *this; }
    ~Aggregate() { std::cout << "Destructor\n"; }
};

static_assert(std::is_aggregate_v<Aggregate>);

static_assert(std::is_copy_assignable<Aggregate>::value);
static_assert(std::is_default_copy_assignable<Aggregate>::value == false);
static_assert(std::is_trivially_copy_assignable<Aggregate>::value == false);

Замечания

Декларация и определение

Отметим, что для следующего кода установить истинность type trait невозможно:

// a.h
struct DontKnow {
    ~DontKnow();
};

static_assert(std::is_default_destructible<DontKnow>::value == false);

// a.cpp
DontKnow::~DontKnow() = default;

Короткая форма

В духе C++17 вводятся короткие формы:

namespace std {
    template<class T> bool is_default_destructible_v       = is_default_destructible<T>::value;
    template<class T> bool is_default_copy_constructible_v = is_default_copy_constructible<T>::value;
    template<class T> bool is_default_move_constructible_v = is_default_move_constructible<T>::value;
    template<class T> bool is_default_copy_assignable_v    = is_default_copy_assignable<T>::value;
    template<class T> bool is_default_move_assignable_v    = is_default_move_assignable<T>::value;
} // namespace std

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions