【问题标题】:Why destructor cannot be template?为什么析构函数不能是模板?
【发布时间】:2016-02-25 09:08:40
【问题描述】:

我不想在类联合类中使用 SFINAE 禁用用户声明的 destructor,就像在类中的 constructors 一样:

#include <type_traits>
#include <cstdlib>

template< typename A, typename B >
struct U
{
    constexpr U() = default;
    constexpr U(A _a) : a(_a), s{false} {}
    constexpr U(B _b) : b(_b), s{true}  {}

    union
    {
        A a;
        B b;
    };

    bool s;

    template< typename = std::enable_if_t< !(std::is_trivially_destructible< A >{} && std::is_trivially_destructible< B >{}) >, // disable if A and B is trivially destructible
              bool is_noexcept = (noexcept(std::declval< A >().~A()) && noexcept(std::declval< B >().~B())) > // disable if A or B is not destructible
    ~U() noexcept(is_noexcept)
    {
        if (s) {
            b.~B();
        } else {
            a.~A();
        }
    }  
};

int main()
{ 
    struct A {};
    struct B {};
    U< A, B > u;
    static_assert(std::is_literal_type< A >{});
    static_assert(std::is_literal_type< B >{}); // =>
    static_assert(std::is_literal_type< U< A, B > >{});
    return EXIT_SUCCESS;
}

但出现错误:

main.cpp:24:5: error: destructor cannot be declared as a template
    ~U() noexcept(is_noexcept)
    ^
1 error generated.
Failure!

在 C++ 中这种限制有理论上的原因吗?还是只是“遗产”?

【问题讨论】:

  • @DavidHaim 默认构造函数也无法获取参数,但可以是模板。
  • 必须生成析构函数,默认构造函数不需要(类可能有其他构造函数)。
  • 请看我的回答
  • 还没有人提到:如果因为某个对象超出范围而自动调用析构函数,应该使用什么模板类型? ... 正确的。同理解释了为什么不能有正常的函数参数。
  • @deviantfan 整个(包括虚构的析构函数选择)类模板在构造点实例化。范围无关紧要。

标签: c++ templates destructor sfinae


【解决方案1】:

任何类U 都可以有一个且只有一个析构函数,该析构函数在该类中以名称~U() 声明并且完全不接受任何参数。

模板函数指定一个函数族,其中“族”描述了包含一个或多个函数的集合,成员数量没有上限。

这两个概念是相互排斥的。一个类不能同时拥有一个析构函数和一系列析构函数。

【讨论】:

  • 使用 SFINAE 我可以制作“一系列函数”以在一般情况下提供唯一可行的函数。
  • 我看不出矛盾:乍一看,引入析构函数选择的可能性(或使编译器有条件地提供默认值)没有不可逾越的障碍。
  • 那是因为你不明白模板定义和模板实例化的区别。模板是允许多个实例化的规范,具体取决于所使用的参数。如果只允许一个函数的一个实例化,则该函数不能是模板。 SFINAE 仅在随后实例化有效模板时才起作用。检查模板是否有效时会出现您看到的错误消息。如果模板本身无效,则在尝试实例化模板之前代码将被拒绝。
  • 最后一句话很有意义。真的是这样。也许有了概念,我就会得到想要的可能性。
  • 不知道“期望的可能性”是什么,我只能猜测。但我怀疑您可能需要做的是对模板结构U 进行(部分)专业化。 U 的每个特化都有自己的析构函数。但是析构函数本身不会被模板化。
【解决方案2】:

作为一种解决方法,您可以添加层和专业化,例如:

template <typename A, typename B,
          bool trivially_destructible = std::is_trivially_destructible<A>{}
                                    && std::is_trivially_destructible<B>{},
          bool is_no_except = noexcept(std::declval<A>().~A())
                        && noexcept(std::declval<B>().~B())>
struct UdestructorImpl;

template <typename A, typename B, bool is_no_except>
struct UdestructorImpl<A, B, true, is_no_except>
{
    ~UdestructorImpl() noexcept(is_no_except) = default;
}

template <typename A, typename B, bool is_no_except>
struct UdestructorImpl<A, B, false, is_no_except>
{
    ~UdestructorImpl() noexcept(is_no_except) = delete;
}

template< typename A, typename B > struct U : UdestructorImpl<A, B>
{
    // Implementation
};

【讨论】:

  • 使用 EBO 或这种技术是严格的,但过于冗长。
  • Bases 是可变参数类模板的唯一选择。
猜你喜欢
  • 2021-09-25
  • 1970-01-01
  • 2019-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-05
  • 2017-12-17
  • 2010-12-20
相关资源
最近更新 更多