cpp-ru / ideas

Идеи по улучшению языка C++ для обсуждения
https://cpp-ru.github.io/proposals
Creative Commons Zero v1.0 Universal
89 stars 0 forks source link

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

Open pavelkryukov opened 2 years ago

pavelkryukov commented 2 years ago

Предлагается ввести следующие 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 тогда и только тогда, когда соответствующая функция определяется компилятором:

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
pavelkryukov commented 2 years ago

Похоже, есть в Reflection TS:

template <SpecialMemberFunction T> struct is_implicitly_declared; template <SpecialMemberFunction T> struct is_defaulted; All specializations of these templates shall meet the UnaryTypeTrait requirements (20.10.1). If their template parameter reflects a special member function that is implicitly declared (for is_implicitly_declared) or that is defaulted in its first declaration (for is_defaulted), the base characteristic of the respective template specialization is true_type, otherwise it is false_type.