简体   繁体   中英

Potential C++ compiler optimization with/without throw/noexcept function

Assume the following class:

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;
}

I know that this is not the best way to implement operator= but that's on purpose, because my question is about these two lines:

    m_content = nullptr;
    m_content = getCopiedContent(rhs);

Can be that the compiler will optimize out m_content = nullptr; even though getCopiedContent is not defined as throw() or noexcept :

other_type* getCopiedContent(const Example& obj);

On one hand the compiler may assume that if right after m_content = nullptr; I overwrite the value of m_content with the return value of getCopiedContent , it may optimize out the whole m_content = nullptr; expression. On the other hand if the compiler optimizes it out and getCopiedContent throws an exception, m_content will contain a non valid value.

Does C++ standard state anything regarding such scenario?

Can be that the compiler will optimize out m_content = nullptr; even though getCopiedContent is not defined as throw() or noexcept:

Yes. This is a redundant operation with no side-effects. Any self-respecting compiler will optimise the redundant store away. In fact you'll have to work really hard to keep the redundant store from being optimised out, such as:

  1. make it std::atomic (if it's atomic, writes are obliged to to transmitted to other threads)
  2. make it volatile
  3. surround the write with some kind of memory barrier (eg lock a std::mutex ) for the same reasons as (1)

On the other hand if the compiler optimizes it out and getCopiedContent throws an exception, m_content will contain a non valid value

Good observation. The compiler is permitted to perform the write of nullptr in the exception handler. ie it may re-order instructions in order to save operations provided the total outcome was 'as if' it did not.

Does C++ standard states anything regarding such scenario?

Yes. It has the 'as-if' rule. While reasoning about one thread, the visible outcome must be 'as-if' each of your statements were executed sequentially with no optimisations against a non-pipelined, non-cached, very simple memory model. Note that no computer produced in the past 20 years is actually this simple, but the outcome of the program must be as if it were.

There is one exception to this - copy elision. Side effects of eliding redundant copies under certain circumstances do not need to be preserved. For example, while eliding copies of arguments that are temporaries and during RVO.

I believe this is called Dead Store Elimination .

I don't know if compiler optimizations are included in the standards except for the as-if rule .

Here is the code for Clang for the dead store elimination. http://www.llvm.org/docs/doxygen/html/DeadStoreElimination_8cpp_source.html This will only do block local ones.

Maybe there are some tools that can inline the function and do the analysis as block local to see if that nullptr store can be eliminated. Obviously, a throw in the function somewhere would make the analysis keep the nullptr store.

Obviously, the scenario you described violates the as-if rule so that is not standard compliant.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM