изобретая дискриминационный союз/меченый вариант я делаю вывод, что существует особая необходимость в такой функции, как «сделать деструктор тривиально на определенных условиях во время компиляции». Я имею в виду какой-то SFINAE или что-то вроде (псевдокод):тривиальный деструктор Условно
template< typename ...types >
struct X
{
~X() = default((std::is_trivially_destructible<types>{} && ...))
{
// non-trivial code here
}
};
Это означает, что если условие default(*)
является true
, то определение деструктора равно ~X() = default;
, но если это false
то { // ... }
тело вместо.
#pragma once
#include <type_traits>
#include <utility>
#include <experimental/optional>
#include <cassert>
template< typename ...types >
class U;
template<>
class U<>
{
U() = delete;
U(U &) = delete;
U(U const &) = delete;
U(U &&) = delete;
U(U const &&) = delete;
void operator = (U &) = delete;
void operator = (U const &) = delete;
void operator = (U &&) = delete;
void operator = (U const &&) = delete;
};
template< typename first, typename ...rest >
class U< first, rest... >
{
struct head
{
std::size_t which_;
first value_;
template< typename ...types >
constexpr
head(std::experimental::in_place_t, types &&... _values)
: which_{sizeof...(rest)}
, value_(std::forward<types>(_values)...)
{ ; }
template< typename type >
constexpr
head(type && _value)
: head(std::experimental::in_place, std::forward<type>(_value))
{ ; }
};
using tail = U<rest...>;
union
{
head head_;
tail tail_;
};
template< typename ...types >
constexpr
U(std::true_type, types &&... _values)
: head_(std::forward<types>(_values)...)
{ ; }
template< typename ...types >
constexpr
U(std::false_type, types &&... _values)
: tail_(std::forward<types>(_values)...)
{ ; }
public :
using this_type = first; // place for recursive_wrapper filtering
constexpr
std::size_t
which() const
{
return head_.which_;
}
constexpr
U()
: U(typename std::is_default_constructible<this_type>::type{}, std::experimental::in_place)
{ ; }
U(U &) = delete;
U(U const &) = delete;
U(U &&) = delete;
U(U const &&) = delete;
template< typename type >
constexpr
U(type && _value)
: U(typename std::is_same< this_type, std::decay_t<type> >::type{}, std::forward<type>(_value))
{ ; }
template< typename ...types >
constexpr
U(std::experimental::in_place_t, types &&... _values)
: U(typename std::is_constructible< this_type, types... >::type{}, std::experimental::in_place, std::forward<types>(_values)...)
{ ; }
void operator = (U &) = delete;
void operator = (U const &) = delete;
void operator = (U &&) = delete;
void operator = (U const &&) = delete;
template< typename type >
constexpr
void
operator = (type && _value) &
{
operator std::decay_t<type> &() = std::forward<type>(_value);
}
constexpr
explicit
operator this_type &() &
{
assert(sizeof...(rest) == which());
return head_.value_;
}
constexpr
explicit
operator this_type const &() const &
{
assert(sizeof...(rest) == which());
return head_.value_;
}
constexpr
explicit
operator this_type &&() &&
{
assert(sizeof...(rest) == which());
return std::move(head_.value_);
}
constexpr
explicit
operator this_type const &&() const &&
{
assert(sizeof...(rest) == which());
return std::move(head_.value_);
}
template< typename type >
constexpr
explicit
operator type &() &
{
return static_cast< type & >(tail_);
}
template< typename type >
constexpr
explicit
operator type const &() const &
{
return static_cast< type const & >(tail_);
}
template< typename type >
constexpr
explicit
operator type &&() &&
{
//return static_cast< type && >(std::move(tail_)); // There is known clang++ bug #19917 for static_cast to rvalue reference.
return static_cast< type && >(static_cast< type & >(tail_)); // workaround
}
template< typename type >
constexpr
explicit
operator type const &&() const &&
{
//return static_cast< type const && >(std::move(tail_));
return static_cast< type const && >(static_cast< type const & >(tail_));
}
~U()
{
if (which() == sizeof...(rest)) {
head_.~head();
} else {
tail_.~tail();
}
}
};
// main.cpp
#include <cstdlib>
int
main()
{
U< int, double > u{1.0};
assert(static_cast<double>(u) == 1.0);
u = 0.0;
assert(static_cast<double>(u) == 0.0);
U< int, double > w{1};
assert(static_cast<int>(w) == 1);
return EXIT_SUCCESS;
}
В этом примере для создания класса U
буквальным типа (в случае first, rest...
является все тривиальной разрушаемостью) можно определить почти так же, как U
класса (V
), но без определения деструктора ~U
(т. Е. Является литеральным типом, если все нисходящие типы являются литералами). Затем определите тип шаблона псевдоним
template< typename ...types >
using W = std::conditional_t< (std::is_trivially_destructible<types>{} && ...), V<types...>, U<types...> >;
и переопределить using tail = W<rest...>;
в обоих U
и V
. Следовательно, существует два почти одинаковых класса, отличается только наличием деструктора. Вышеуказанный подход требует чрезмерного дублирования кода.
Проблема также связана с тривиально копирующими/переносными присваиваемыми типами и operator =
, а также всеми другими условиями для типа std::is_trivially_copyable
. 5 условий дают полностью 2^5 комбинации для реализации.
Есть ли готовый к использованию техники (и менее многословной, то описанной выше одного) выражающейся настоящий C++ я скучаю, или, возможно, в ближайшее время предложения ?
Другим мыслимым подходом является (особенность языка), чтобы отметить деструктор как constexpr
и предоставить компилятору проверить, является ли тело эквивалентным тривиальному во время создания экземпляра или нет.
UPDATE:
код упрощен как указано в комментариях: union
стал union
-как класса. Удалены noexcept
спецификаторы.
Я не знаю сценарий, который может привести к такой конструкции деструктора. Я также * чувствую *, что бы ни был этот сценарий, который можно было бы решить при правильном использовании RAII (реализованными аргументами шаблона 'types ...'). – Nawaz
Я не понимаю, почему вы хотите/нуждаетесь в 'X' для хранения значений и управления уничтожением напрямую. Возможно, есть элемент или базовый шаблон для значения 'std :: is_trivially_destructible', тогда вы можете специализировать деструктор' Member_Or_Base 'для предикации желаемого уничтожения. И это слишком много кода для публикации - когда у вас есть конкретная проблема, создайте минимальный код, иллюстрирующий это. –
@Nawaz Сценарий возникает, когда нам нужно определить * условно-литерал * класс.Я предоставляю полный пример и одно возможное ошибочное решение. – Orient