【发布时间】:2015-09-02 03:59:47
【问题描述】:
发明一个可区分联合/标记变体 我的结论是,“在编译时使析构函数在某些条件下变得微不足道”这样的特性是特别需要的。我的意思是某种 SFINAE 或类似的东西(伪代码):
template< typename ...types >
struct X
{
~X() = default((std::is_trivially_destructible< types >{} && ...))
{
// non-trivial code here
}
};
这意味着如果default(*)中的条件是true,那么析构函数的定义就等于~X() = default;,但是如果是false,则使用{ // ... } body。
#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... > >;
并在U 和V 中重新定义using tail = W< rest... >;。因此,有两个几乎相同的类,不同之处仅在于析构函数的存在。上述方法需要过多的代码重复。
该问题还涉及简单的复制/移动可分配类型和operator =,以及类型为std::is_trivially_copyable 的所有其他条件。 5 个条件总共给出了 2^5 种组合来实现。
我想念当前的C++或即将推出的提案,是否有任何现成可用的技术(并且不那么冗长,然后在上面描述)?
另一种可行的方法是(语言特性)将析构函数标记为constexpr,并授权编译器在实例化期间测试主体是否等同于平凡的。
更新:
如 cmets 中指出的那样简化代码:union 变成了类似union 的类。删除了 noexcept 说明符。
【问题讨论】:
-
我不知道会导致这种析构函数设计的场景。我也感觉无论是哪种情况,都可以通过正确使用 RAII 来解决(由模板参数
types...实现)。 -
我不明白您为什么想要/需要
X存储值并直接管理销毁。也许在std::is_trivially_destructible值上有一个成员或基础模板,然后您可以专门化Member_Or_Base<false>的析构函数来执行所需的破坏。而且要发布的代码太多了 - 当您遇到特定问题时,请创建最少的代码来说明它。 -
@Nawaz 当我们需要定义 conditionally literal 类时,就会出现这种情况。我提供了完整的示例和一个可能存在缺陷的解决方案。
-
@TonyD 不要看
X,而是看U。在 C++unions 中不能有一个基类作为基类。 -
@Nawaz 你知道
U是union,但不是struct或class?如果任何底层类型不可轻易破坏,则必须提供析构函数。
标签: c++ c++14 destructor c++20 variant