Andrei Alexandrescu关于错误处理的讨论:
请参阅C++ and Beyond 2012: Andrei Alexandrescu - Systematic Error Handling in C++(约30分钟)
Andrei提供以下代码:
~Expected()
{
using std::exception_ptr;
if (gotHam) ham.~T();
else spam.~exception_ptr();
}
此析构函数正在清理union
,其中包含某种类型T
或std::exception_ptr
。使用placement new
填充联合。
using std::exception_ptr;
是必要的,因为以下代码无法解析:
else spam.~std::exception_ptr();
这意味着如果需要在不同的命名空间中显式调用类的析构函数,则始终需要使用using指令。
为什么第二个例子没有解析?
以下代码是否是有效的替代方案?
else delete spam;
这是否与显式调用std::exception_ptr
答案 0 :(得分:10)
Andrei可能使用using std::exception_ptr;
,因为他的编译器坏了。
没有必要。如果没有它,spam.~exception_ptr();
应该编译得很好。
3.4.5 / 3。如果unqualified-id是~type-name,则在整个postfix-expression的上下文中查找type-name。如果对象表达式的类型T是类类型C,则类型名称也在类C的范围内查找。
它确实与gcc编译。
如果由于某种原因需要使用qualified-name,spam.std::exception_ptr::~exception_ptr();
也会编译。
答案 1 :(得分:8)
这里的问题是~std::exception_ptr()
实际上并不是您要调用的函数的名称,而只是~exception_ptr()
。并且,由于它属于不同命名空间中的类,因此无法访问(编辑:虽然它应该可以根据C ++ 11标准中的§3.4.5/ 3访问,正如nm在他的回答中指出的那样,但是微软编译器就是这样的。)
您可以选择将类放入命名空间:使用限定类名进行显式调用:
else spam.std::exception_ptr::~exception_ptr(); // This is legal
至于你的第二个问题,正如R. Martinho Fernandes在评论中正确解释的那样,调用delete
运算符并不等同于只调用析构函数:它还调用了笨拙命名的函数operator delete()
。
答案 2 :(得分:3)
语法spam.~std::exception_ptr
是不允许的,因为语法要求id-expression而~std::exception_ptr
不是一个,正如Gorpik指出的那样,你需要spam.std::exception_ptr::~exception_ptr()
。但是我不理解为什么需要限定,在描述语法的子句中,提醒
因为类的名称是在其类作用域中插入的(第9节),所以类的名称也被认为是该类的嵌套成员。
所以我认为即使没有using子句,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;
}
因此安德烈被跟踪(通过他正在使用的编译器中的错误或忘记类名在类的范围内导入的事实)尝试一个不需要解决的问题。