为什么不 ~ 后跟 :: 解析

Why doesn't ~ followed by :: parse

本文关键字:解析 后跟 为什么不      更新时间:2023-10-16

在Andrei Alexandrescu关于错误处理的演讲中:

参见c++和超越2012:Andrei Alexandrescu - c++中的系统错误处理(大约30分钟)

Andrei给出了以下代码:

~Expected()
{
    using std::exception_ptr;
    if (gotHam) ham.~T();
    else spam.~exception_ptr();
}

析构函数正在清理包含某种类型Tstd::exception_ptrunion。使用placement new填充联合。

Andrei接着解释说using std::exception_ptr;是必要的,因为下面的代码不能解析:
    else spam.~std::exception_ptr();

这意味着如果你需要显式调用不同命名空间中的类的析构函数,总是需要有一个using指令。

为什么第二个例子不能解析?

下面的代码是一个有效的替代吗?

    else delete spam;

这是否与显式调用std::exception_ptr

的析构函数具有相同的效果?

Andrei使用using std::exception_ptr;可能是因为他的编译器坏了。

没有必要。没有它,spam.~exception_ptr();应该可以很好地编译。

3.4.5/3。如果非限定id为~type-name,则在整个后缀表达式的上下文中查找类型-name。如果对象表达式的类型T是类类型C,则在类C的作用域中也查找类型名。

它确实使用gcc编译。

如果出于某种原因需要使用限定名称,spam.std::exception_ptr::~exception_ptr();也会编译

这里的问题是,~std::exception_ptr()不是你想调用的函数的真正名称,而只是~exception_ptr()。而且,由于它属于不同命名空间中的类,它是不可访问的(编辑:尽管根据c++ 11标准中的§3.4.5/3,它应该是可访问的,正如n.m.在他的回答中指出的那样,但是微软编译器是这样做的)。

您可以选择将类带入您的名称空间:使用限定类名进行显式调用:

else spam.std::exception_ptr::~exception_ptr(); // This is legal

至于你的第二个问题,正如R. Martinho Fernandes在评论中正确解释的那样,调用delete运算符并不仅仅等同于调用析构函数:它还调用了名称笨拙的函数operator delete()

语法spam.~std::exception_ptr是不允许的,因为语法要求id表达式,而~std::exception_ptr不是,正如Gorpik指出的那样,您需要spam.std::exception_ptr::~exception_ptr()。但是我不明白为什么需要限定,在描述语法的子句中,提醒了

因为类的名称插入到类的作用域中(第9条),所以类的名称也被认为是该类的嵌套成员。

所以我认为spam.~exception_ptr()应该是有效的,即使没有使用子句。顺便说一句

namespace ns {
struct Foo {};
}
void f()
{
    ns::Foo x;
    x.~Foo();
}

用我所能访问的所有g++(包括非常旧的2.95)干净地编译。这似乎证实了我的观点,如果它不能在c++ 11更新的联合类型上下文中工作,那就是实现中的错误。

编辑,在g++ 4.7.1下,使用-std=c++11也可以编译以下代码。

namespace ns {
struct Foo {};
}
struct Bar {};
union U {
    ns::Foo f;
    Bar b;
};
struct C {
    bool b;
    U u;
    ~C() {
        if (b)
            u.f.~Foo();
        else
            u.b.~Bar();
    }                
};
void f()
{
    C c;
}

所以Andrei已经被侧边跟踪(要么是他正在使用的编译器中的错误,要么是忘记了类名在类的作用域内导入的事实)尝试解决一个不需要解决的问题。