C++ 在奇怪的情况下跳过内部捕捉

C++ 在奇怪的情况下跳过内部捕捉,c++,try-catch,c++17,C++,Try Catch,C++17,我在一个我一直在研究的代码库中发现了一个非常奇怪的bug,直到最近我才能够从中分离并创建一些可复制的东西。错误在于当throw\u threshold为偶数时,跳过simulate\u container\u init中的捕获throw\u threshold用于ex\u trigger中,以模拟在容器构造或分配等情况下复制对象时的抛出,以进行单元测试 我最初认为这是一个MSVC编译器错误(在撰写本文时为14.16),但在GCC 7.1和Clang 3.9.1(几乎是支持我的示例的最古老的版本)

我在一个我一直在研究的代码库中发现了一个非常奇怪的bug,直到最近我才能够从中分离并创建一些可复制的东西。错误在于当
throw\u threshold
为偶数时,跳过
simulate\u container\u init
中的捕获
throw\u threshold
用于
ex\u trigger
中,以模拟在容器构造或分配等情况下复制对象时的抛出,以进行单元测试

我最初认为这是一个MSVC编译器错误(在撰写本文时为14.16),但在GCC 7.1和Clang 3.9.1(几乎是支持我的示例的最古老的版本)上成功地复制了它之后,我不确定该如何处理它,当
throw\u threshold
为奇数时,代码显示正确且功能正常

#include <cstdint>
#include <atomic>
#include <memory>
#include <random>
#include <iostream>
#include <exception>
#include <type_traits>

// Used to trigger an exception after a number of constructions have occurred.
struct ex_trigger
{
private:
    static std::atomic<uint32_t> ctor_count;
    static std::atomic<uint32_t> throw_threshold;

public:
    static inline void set_throw_threshold(uint32_t value) noexcept
    {
        throw_threshold.store(value, std::memory_order_release);
    }

    std::atomic<uint32_t> value;

    inline ex_trigger(const ex_trigger& source) :
        value(source.value.load(std::memory_order_acquire))
    {
        if (ctor_count.load(std::memory_order_acquire) >= 
            throw_threshold.load(std::memory_order_acquire)) {
            throw std::logic_error("test");
        }

        ctor_count.fetch_add(1);
    }
    inline ex_trigger(uint32_t value) noexcept :
        value(value)
    {
        ctor_count.fetch_add(1);
    }
};
std::atomic<uint32_t> ex_trigger::ctor_count;
std::atomic<uint32_t> ex_trigger::throw_threshold;

// Simulates the construction of a container by copying an initializer list.
template<class T>
inline void simulate_container_ctor(std::initializer_list<T> values) {
    // Intentionally leaked to simplify this example.
    // Alignment of T is completely ignored for simplicity.
    auto sim_data = static_cast<T*>(
        ::operator new(sizeof(T) * values.size()));

    for (auto value : values) {
        if constexpr (std::is_nothrow_copy_constructible_v<T>) {
            new (sim_data++) T(value);
        } else {
            try {
                new (sim_data++) T(value);
            } catch (...) {
                // Placeholder for cleanup code which is sometimes skipped.
                std::cout << "caught [inner]\n";
                throw;
            }
        }
    }
}

int main()
{
    // The "inner" catch handler within simulate_container_init will be skipped when the argument
    // to set_throw_threshold is even, but otherwise appears to work correctly when it's odd. Note
    // that the argument must be between 10-20 for an exception to be triggered in this example.
    ex_trigger::set_throw_threshold(11);
    try {
        simulate_container_ctor({
            ex_trigger(1),
            ex_trigger(2),
            ex_trigger(3),
            ex_trigger(4),
            ex_trigger(5),
            ex_trigger(6),
            ex_trigger(7),
            ex_trigger(8),
            ex_trigger(9),
            ex_trigger(10)
        });
    } catch (const std::logic_error&) {
        std::cout << "caught [outer]\n";
    }
    std::cin.get();
    return 0;
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
//用于在多次构造发生后触发异常。
结构exu触发器
{
私人:
静态标准::原子计数;
静态std::原子抛出_阈值;
公众:
静态内联无效集\u抛出\u阈值(uint32\u t值)无异常
{
throw\u threshold.store(值、标准::内存\u顺序\u释放);
}
std::原子值;
内联ex_触发器(常量ex_触发器和源):
值(source.value.load(std::memory\u order\u acquire))
{
如果(ctor_count.load(std::内存_order_acquire)>=
throw\u threshold.load(标准::内存\u顺序\u获取)){
抛出标准::逻辑_错误(“测试”);
}
ctor_count.fetch_add(1);
}
内联ex_触发器(uint32_t值)无异常:
价值(价值)
{
ctor_count.fetch_add(1);
}
};
std::原子ex_触发器::ctor_计数;
std::原子ex_触发器::抛出_阈值;
//通过复制初始值设定项列表来模拟容器的构造。
模板
内联无效模拟容器(标准::初始值设定项列表值){
//故意泄露以简化此示例。
//为了简单起见,完全忽略了T的对齐。
自动模拟数据=静态模拟(
::运算符new(sizeof(T)*values.size());
用于(自动值:值){
if constexpr(std::is_nothrow_copy_constructible_v){
新(sim_data++)T(值);
}否则{
试一试{
新(sim_data++)T(值);
}捕获(…){
//清除代码的占位符,有时会被跳过。

std::cout问题是(auto-value:values){
copy构造一个临时的
ex\u触发器
,该触发器将在内部异常处理程序之外抛出异常。解决方法是迭代(auto-const&value:values){的引用{copy构造一个临时的
ex_触发器
,该触发器将在内部异常处理程序外部引发异常。修复方法是迭代(auto const&value:values)的引用
{

有时候真正需要的是一双新鲜的眼睛。谢谢你的帮助。有时候真正需要的是一双新鲜的眼睛。谢谢你的帮助。