【问题标题】:Empty destructor vs literal destructor空析构函数与文字析构函数
【发布时间】:2013-01-10 10:19:09
【问题描述】:

考虑以下代码:

#include <iostream>

class Test
{
    public:
        constexpr Test(const int x) : _x(x) {}
        constexpr int get() const {return _x;}
        ~Test() {} // HERE
    protected:
        const int _x;
};

int main()
{
    static constexpr Test test(5);
    return 0;
}

如果我删除HERE 行,代码编译良好,但如果我定义一个空析构函数,则会导致编译错误,指出Test 是非文字的。

为什么空析构函数和没有析构函数有什么区别?

编辑:另一个相关问题:如果空和文字析构函数不同,如何定义受保护的文字析构函数?

【问题讨论】:

  • 在 constexpr 对象中不能有非平凡的析构函数。
  • 受保护的析构函数 = 几乎不是一个好主意。
  • 你可以做~Test() = default;,我想。
  • @Vincent 在这种情况下,为什么不使用 构造函数 protected 而不是析构函数?
  • @KonradRudolph 如果某个类打算成为其他类的基类,则其析构函数应该是虚拟的或受保护的,以防止通过基指针删除派生对象时出现 UB。

标签: c++ c++11 destructor constexpr user-defined-literals


【解决方案1】:

引自 n3376

7.1.5/9

对象声明中使用的 constexpr 说明符将对象声明为 const。这样的对象应具有 文字类型并且应该被初始化。如果它由构造函数调用初始化,则该调用应为常量 表达式

3.9/10

如果满足以下条件,则类型是文字类型:

它有一个微不足道的析构函数...

12.4/5

如果析构函数不是用户提供的,并且满足以下条件,则析构函数是微不足道的:

——析构函数不是虚拟的,

——其类的所有直接基类都有普通的析构函数,并且

——对于其类的所有属于类类型(或其数组)的非静态数据成员,每个这样的 类有一个微不足道的析构函数。

否则,析构函数是不平凡的。

clang 诊断确实提供更多信息:

error: constexpr variable cannot have non-literal type 'const C'

'C' is not literal because it has a user-provided destructor

【讨论】:

  • Clang:1254,其他人:32
  • @chris:对不起,我不知道,但你的评论是什么意思?
  • @axeoth,与其他编译器相比,Clang 的错误是可读的和/或有用的。
【解决方案2】:

没有析构函数会导致编译器添加一个微不足道的析构函数,这在规范中定义不正确,但基本上什么都不做。

如果你指定一个析构函数,它不会添加平凡的析构函数。你的析构函数很重要。

在您的情况下,Test::~Test() { } 看起来非常琐碎,但这是对您所看到内容的人类解释。更进一步,如何:

Test::~Test()
{
    int a = 5;
}

我们可以看到优化器可以优化出a,所以它显然什么都不做。怎么样:

Test::~Test()
{
    for (int i = 0; i < 1000; i += 2) {
        if ((i % 2) == 1)
            doSomeSideEffect(); // like throwing or setting a global
    }
}

我们可以看到i 永远不会是奇数,所以析构函数什么都不做。

规范必须定义什么可以是 constexpr,什么不能。他们没有深入定义“什么都不做”的兔子洞,而是简单地声明,唯一对 constexpr 足够好的无所事事析构函数是编译器提供的琐碎析构函数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-08-29
    • 2023-03-10
    • 2014-03-02
    • 2010-09-19
    • 2012-11-12
    • 2013-07-07
    • 2013-07-28
    相关资源
    最近更新 更多