3

次のような類似のコード スニペットをいくつか見てきました。

struct MyExcept : std::exception {
    explicit MyExcept(const char* m) noexcept : message{m} {}

    const char* what() const noexcept override {
        return message;
    }

    const char* message;
};

void foo() {
    std::string error;

    error += "Some";
    error += " Error";

    throw MyExcept{error.c_str()};
}

int main() {
    try {
        foo();
    } catch (const MyExcept& e) {
        // Is this okay?
        std::cout << e.message << std::endl;
    }
}

コメント の次の行では、を使用して関数Is this okay?に割り当てられた C スタイルの文字列を読み取ります。スタックの巻き戻しで文字列が破壊されるため、これは未定義の動作ですか?foostd::string


それが実際に未定義の動作である場合、main関数をこれに置き換えたらどうなるでしょうか?

int main() {
    foo();
}

キャッチがないため、コンパイラは強制的にスタックをアンワインドしませんが、結果をwhat()コンソールに出力してプログラムを中止します。それで、それはまだ未定義の動作ですか?

4

3 に答える 3

2

Your first snippet has undefined behavior. [exception.ctor]/1:

As control passes from the point where an exception is thrown to a handler, destructors are invoked by a process, specified in this section, called stack unwinding.

Here, the destructor or error is called, causing the c_str() to become a dangling pointer. Later dereferencing it, when you use std::cout for instance, is undefined behavior.

Your second snippet is perfectly fine. There is no reason why it would be undefined behavior. You never actually call what, or do anything else that might result in undefined behavior. The only thing not defined by the Standard is if stack unwinding happens or not, [except.terminate]/2:

In the situation where no matching handler is found, it is implementation-defined whether or not the stack is unwound before std​::​terminate() is called.

于 2017-07-20T16:36:09.663 に答える