11

发明一个可区分的联合/标记变体我得出结论,特别需要“在编译时的某些条件下使析构函数变得微不足道”这样的功能。我的意思是某种 SFINAE 或类似的东西(伪代码):

template< typename ...types >
struct X
{
    ~X() = default((std::is_trivially_destructible< types >{} && ...))
    {
        // non-trivial code here
    }
};

这意味着如果条件 in default(*)is 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...几乎相同的内容,但没有定义析构函数(即,如果所有降序类型都是文字,则为文字类型)。然后定义模板类型别名UV~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_copyable5 个条件总共提供了 2^5 种组合来实现

是否有任何现成的技术(并且不那么冗长,然后在上面描述)可以在我想念的当前C++中表达,或者可能很快就会提出建议

另一种可以考虑的方法是(语言特性)将析构函数标记为constexpr并授予编译器以在实例化期间测试主体是否等同于平凡的。

更新:

正如评论中指出的那样简化了代码:union变成了类似union的类。删除了noexcept说明符。

4

1 回答 1

3

条件析构函数可以通过具有模板专业化的附加中间层来实现。例如:

Coliru 上的现场演示

#include <type_traits>
#include <iostream>
#include <vector>

using namespace std;

template<typename T>
class storage
{
    aligned_storage_t<sizeof(T)> buf;

    storage(storage&&) = delete;
public:
    storage()
    {
        new (&buf) T{};
    }
    T &operator*()
    {
        return *static_cast<T*>(&buf);
    }
    void destroy()
    {
        (**this).~T();
    }
};

template<typename T, bool destructor>
struct conditional_storage_destructor 
{
    storage<T> x;
};

template<typename T>
struct conditional_storage_destructor<T, true> : protected storage<T>
{
    storage<T> x;

    ~conditional_storage_destructor()
    {
        x.destroy();
    }
};

template<typename T>
class wrapper
{
    conditional_storage_destructor<T, not is_trivially_destructible<T>::value> x;
public:
    T &operator*()
    {
        return *(x.x);
    }
};

int main()
{
    static_assert(is_trivially_destructible< wrapper<int> >::value);
    static_assert(not is_trivially_destructible< wrapper<vector<int>> >::value);

    cout << "executed" << endl;
}
于 2015-07-01T16:57:24.533 回答