【问题标题】:Why doesn't ~ followed by :: parse为什么不 ~ 后跟 :: parse
【发布时间】:2012-12-18 20:41:12
【问题描述】:

在 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,它包含一些类型Tstd::exception_ptr。联合使用placement new 填充。

Andrei 然后解释说using std::exception_ptr; 是必要的,因为以下代码无法解析:

    else spam.~std::exception_ptr();

这意味着如果您需要显式调用不同命名空间中类的析构函数,则始终需要使用 using 指令。

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

以下代码会是一个有效的替代方案吗?

    else delete spam;

这和显式调用std::exception_ptr的析构函数有同样的效果吗

【问题讨论】:

  • 关于后一个问题,没有。 delete 包括调用析构函数调用operator delete
  • 我明白为什么它不能与 std:: 一起使用,但我很惊讶你需要将它带入命名空间。 spam 已经是 exception_ptr 类型,而您只是为使用放置 new 分配的内存调用析构函数? spam 可能是一个引用,而不是你使用的指针。不是 -> 但如果使用了新展示位置,则删除 &spam 也无效。
  • @R. Martinho Fernandes,谢谢,这个问题提供了一些进一步的解释。 stackoverflow.com/questions/6783993/placement-new-and-delete
  • 您可以使用 aias 模板解决它:template<typename T> using alias = T; 然后写 spam.~alias<std::exception_ptr>()

标签: c++ c++11


【解决方案1】:

这里的问题是~std::exception_ptr() 并不是您要调用的函数的名称,而只是~exception_ptr()。而且,由于它属于不同命名空间中的一个类,所以它是不可访问的(编辑:虽然它应该可以根据 C++11 标准中的 §3.4.5/3 进行访问,正如 nm 在他的回答中指出的那样,但是 Microsoft编译器会以这种方式运行)。

除了将类引入命名空间之外,您还有另一种选择:使用限定的类名进行显式调用:

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

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

【讨论】:

  • 记住这会抑制虚函数调用机制。所以如果spam 是一个引用或指针(spam->),这将停止工作。
【解决方案2】:

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

没必要。 spam.~exception_ptr(); 没有它应该编译得很好。

3.4.5/3。如果 unqualified-id 是 ~type-name,则在整个后缀表达式的上下文中查找类型名称。如果对象表达式的类型 T 是类类型 C,则类型名称也在类 C 的范围内查找。

它确实可以用 gcc 编译。

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

【讨论】:

  • 啊,我没有读过标准中的那个条款。 Andrei 一定用过微软的编译器,我也用过这个编译器。
【解决方案3】:

语法 spam.~std::exception_ptr 是不允许的,因为语法要求一个 id 表达式,而 ~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;
}

所以 Andrei 一直在试图解决一个不需要解决的问题(可能是因为他正在使用的编译器中的一个错误,或者是因为忘记了类名是在类的范围内导入的事实)。

【讨论】:

  • VS2010(我正在使用的编译器)按问题中的说明工作。阅读第 12.4/12 节,我了解到显式调用必须使用类型名称(在本例中为 std::exception_ptr),但这也应该是被调用函数的名称,这是不正确的。无论如何,这对我来说不太清楚:我不知道在这种情况下谁是对的,VS(和 Alexandrescu)还是 GCC。
  • 啊,阅读n.m.的回答,我没有看过标准中的那个条款。现在我认为VS是错误的。
猜你喜欢
  • 1970-01-01
  • 2022-01-06
  • 2021-07-10
  • 1970-01-01
  • 2019-05-09
  • 2012-11-10
  • 2016-04-02
  • 2016-03-12
  • 1970-01-01
相关资源
最近更新 更多