有条件的琐碎的析构函数

Ori*_*ent 9 c++ destructor variant c++11 c++14

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

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

这意味着如果条件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;
}
Run Code Online (Sandbox Code Playgroud)

在这个使类U成为文字类型的例子中(如果first, rest...是所有可以轻易破坏的),可以定义几乎与Uclass(V)相同,但没有析构函数的定义~U(即,如果所有降序类型都是文字,则为文字类型) ).然后定义模板类型别名

template< typename ...types >
using W = std::conditional_t< (std::is_trivially_destructible< types >{} && ...), V< types... >, U< types... > >;
Run Code Online (Sandbox Code Playgroud)

和重新定义using tail = W< rest... >;在两个U和V.因此,有两个几乎相同的类,仅在析构函数存在时不同.上述方法需要过多的代码重复.

该问题还涉及简单地复制/移动可分配类型以及类型的operator =所有其他条件std::is_trivially_copyable.5个条件总共给出了2 ^ 5个组合来实现.

是否有任何现成的技术(并且更简洁,然后在上面描述)可以在目前的C++中表达我想念,或者可能即将推出的提案?

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

更新:

在评论中指出简化的代码:union变成了类似的union.删除了noexcept说明符.

Ran*_*ane 2

值得庆幸的是,有C++20 限制实现这一点几乎会产生原始问题的伪代码,该伪代码既易于理解又易于实现:

\n
#include <type_traits>\n#include <optional>\n#include <string>\n#include <vector>\n\ntemplate< typename ...types >\nstruct X\n{\n    ~X() = default;\n    \n    ~X() requires (!(std::is_trivially_destructible_v<types> && ...))\n    {\n    }\n};\n\nint main()\n{\n    static_assert(std::is_trivially_destructible_v<\n        X<>\n    >);\n    static_assert(std::is_trivially_destructible_v<\n        X<float, int, char>\n    >);\n    static_assert(!std::is_trivially_destructible_v<\n        X<std::vector<int>, std::vector<char>>\n    >);\n    static_assert(!std::is_trivially_destructible_v<\n        X<std::string, int, float>\n    >);\n    static_assert(std::is_trivially_destructible_v<\n        X<std::optional<int>, int, float>\n    >);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

(此处为上帝螺栓链接)

\n

使用重载解析选择适当的析构函数(C++20 标准 \xc2\xa711.4.7.4 [class.dtor]):

\n
\n

在类定义结束时,在该类中声明的预期析构函数之间执行重载决策,并使用空参数列表来选择该类的析构函数,也称为选定析构函数。如果重载解析失败,则程序格式错误。\n析构函数选择不构成对所选析构函数的引用或 odr 使用 ( [basic.def.odr] ),特别是,可以删除所选析构函数([dcl.fct.def.delete])。

\n
\n

整个C++的重载决议用普通话来说是相当长和复杂的,但简单地说,重载决议选择满足约束并且是最受约束的析构函数(C++20标准\xc2\xa712.2.3.1 [超过.匹配.可行]):

\n
\n

从为给定上下文构造的候选函数集中([over.match.funcs]),选择一组可行的函数,通过比较参数转换序列和相关约束([temp.constr .decl])以获得最佳拟合([over.match.best])。可行函数的选择考虑相关约束(如果有)以及自变量和函数参数之间的关系,而不是转换序列的排序。

\n
\n

请注意,此策略也可以应用于其他特殊成员函数(构造函数、赋值运算符等)。尽管P0848R3 - Conditionally Trivial Special Member Functions提案仅在最近的 clang 16 版本中部分实现,而gcc >= 10和MSVC >= VS 2019 16.8完全符合。

\n