有/没有throw/noexcept函数的情况下,C++编译器的潜在优化

10

假设有以下类:

class Example
{
public:
...
    Example& operator=(const Example& rhs);
...
private:
    other_type *m_content;
    size_t m_content_size;
}

Example& Example::operator=(const Example& rhs)
{
    if (this != &rhs)
    {
        delete m_content;
        m_content = nullptr;
        m_content = getCopiedContent(rhs);
    }

    return *this;
}

我知道这不是实现operator=的最佳方法,但这是有意为之的,因为我的问题与以下两行代码有关:

    m_content = nullptr;
    m_content = getCopiedContent(rhs);

编译器可能会优化掉m_content = nullptr;,即使getCopiedContent没有被定义为throw()noexcept

other_type* getCopiedContent(const Example& obj);

一方面,编译器可能会假设在m_content = nullptr;之后,如果我用getCopiedContent的返回值覆盖了m_content的值,它可能会优化掉整个m_content = nullptr;表达式。另一方面,如果编译器将其优化掉并且getCopiedContent抛出异常,m_content将包含一个无效值。

C++标准文件是否对这种情况做出了规定?


2
长话短说:您可以假设您的代码在没有任何优化的情况下运行。唯一的例外是RVO和UB。 - Baum mit Augen
2个回答

5
可以让编译器优化掉 m_content = nullptr; 即使 getCopiedContent 未定义为 throw() 或 noexcept:
是的。这是一项无副作用的冗余操作。任何自重的编译器都会优化掉这个冗余的存储操作。实际上,你需要非常努力地保留这个冗余的存储操作,例如:
1.将它设置为 std::atomic(如果是原子类型,写操作必须传输到其他线程) 2.将其设置为 volatile 3.使用某种内存屏障(例如锁定 std::mutex)来包围写操作,这与 (1) 相同的原因。
另一方面,如果编译器优化掉了这个操作,而 getCopiedContent 抛出异常,那么 m_content 将包含一个无效值。
好观察。编译器允许在异常处理程序中执行 nullptr 的写入。即编译器可以重新排序指令以节省操作,前提是总体结果是“as-if”。
C++标准是否有关于这种情况的规定?
是的。它有“as-if”规则。在考虑一个线程时,可见的结果必须是“as-if”,每个语句都按顺序执行,没有针对非流水线、非缓存、非常简单的内存模型的优化。请注意,过去20年中生产的计算机实际上并不是这么简单的,但程序的结果必须像是这样的。
有一个例外-复制省略。在某些情况下,在省略冗余副本的副作用不需要被保留。例如,在省略临时参数的副本和 RVO 期间。

那种情况在叙述的第二部分中有涉及到。 - Richard Hodges
如果我正确理解了你的回答,编译器可能会执行一些重新排序来优化主线(例如将m_content = nullptr;移动到异常处理程序),但是保证m_content不会包含无效值。我理解得对吗?如果是这样,在异常处理程序中它会把m_content = nullptr;放在哪里?如果相关的try-catch在不同的编译单元中怎么办? - Alex Lop.
1
但是一般情况下,编译器无法证明任意函数是否为“noexcept”,在这种普遍情况下,赋值操作可能不会被优化掉。它可能会被移动到其他地方(比如异常处理程序),但不会被删除。这与简单的“是的。这是一个没有副作用的多余操作。”相矛盾。 - Baum mit Augen
@RichardHodges,强制冗余存储的第四种方法是调用任何不透明函数,在两个存储之间可能会访问此变量。在我们的情况下,这将是类的成员函数,在翻译的不同单元中定义。 - SergeyA
True。编译器在别名的情况下需要保守。 - Richard Hodges
显示剩余3条评论

0

我相信这被称为死代码消除

我不知道编译器优化是否包含在标准中,除了as-if规则

这是Clang的死代码消除代码。 http://www.llvm.org/docs/doxygen/html/DeadStoreElimination_8cpp_source.html 这只会做块本地的。

也许有一些工具可以内联函数并进行分析,以块本地方式查看是否可以消除nullptr存储。显然,在函数中抛出异常会使分析保留nullptr存储。

显然,您描述的情况违反了as-if规则,因此不符合标准。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接