MSVC编译器可能的错误

德米特里·特斯连科(Dmitry Teslenko)

给定在for循环的条件子句中声明的shared_ptr变量,并且for循环主体包含if / continue语句,Microsoft编译器(自2015版开始)在每次循环迭代中生成额外的析构函数调用(共两个)。这导致销毁应由Holder接口用户无法访问的Item对象。请参阅下面的示例代码

namespace
{
    class Item
    {
    public:
        Item(size_t v)
            : value_(v)
        {
            std::cout << "Item(" << value_ << ")" << std::endl;
        }

        ~Item()
        {
            std::cout << "~Item(" << value_ << ")" << std::endl;
        }

        void print() const
        {
            std::cout << "Item::print(" << value_ << ")" << std::endl;
        }

    private:
        size_t value_;

    };

    typedef std::shared_ptr<const Item> ItemCPtr;

    class Holder
    {
    public:
        Holder(size_t n)
        {
            for (size_t i = 0; i < n; ++i)
                items_.emplace_back(new Item(i));
        }

        ItemCPtr getItem(size_t i) const
        {
            if (i < items_.size())
                return items_[i];
            return ItemCPtr();
        }

    private:
        std::vector<ItemCPtr> items_;
    };
}

TEST(Test, Test)
{
    Holder _holder(5);

    std::cout << "before loop" << std::endl;

    for (size_t i = 0; auto _item = _holder.getItem(i); ++i)
    {
        if (!!(i % 2))
            continue;

        _item->print();
    }

    std::cout << "after loop" << std::endl;

    _holder.getItem(1)->print();
    _holder.getItem(3)->print();
}

该产量低于

||   [ RUN      ] Test.Test
||   Item(0)
||   Item(1)
||   Item(2)
||   Item(3)
||   Item(4)
||   before loop
||   Item::print(0)
||   ~Item(1)
||   Item::print(2)
||   ~Item(3)
||   Item::print(4)
||   after loop
||   Item::print(3722304989)
||   Item::print(3722304989)
||   ~Item(0)
||   ~Item(2)
||   ~Item(4)
||   [       OK ] Test.Test (0 ms)

如果我以这种方式将_item声明移出for循环

    auto _item = ItemCPtr();
    for (size_t i = 0; _item = _holder.getItem(i); ++i)

然后我得到这样的预期输出

||   [ RUN      ] Test.Test
||   Item(0)
||   Item(1)
||   Item(2)
||   Item(3)
||   Item(4)
||   before loop
||   Item::print(0)
||   Item::print(2)
||   Item::print(4)
||   after loop
||   Item::print(1)
||   Item::print(3)
||   ~Item(0)
||   ~Item(1)
||   ~Item(2)
||   ~Item(3)
||   ~Item(4)
||   [       OK ] Test.Test (0 ms)

据我了解,getItem应该产生ItemCPtr的副本,并且不能通过Holder接口修改任何Item。但是用户可以销毁循环中五分之二的东西,/标记之间查看~Item(1)~Item(3)析构函数的输出before loopafter loop

这是暴露问题的简单例子。在现实世界中,这将导致难以跟踪的内存损坏问题。

编译器身份:

cmake -G "Visual Studio 14 2015" ..\
-- Selecting Windows SDK version 10.0.14393.0 to target Windows 10.0.19041.
-- The C compiler identification is MSVC 19.0.24210.0
-- The CXX compiler identification is MSVC 19.0.24210.0

操作系统是64位Windows 10

即使禁用优化(/Od默认选项),错误也会显示出来

讲故事的人-Unslander Monica

据我了解,getItem应该产生ItemCPtr的副本,并且不能通过Holder接口修改任何Item。

您的理解是100%准确的。这正是为C ++标准的抽象机描述的行为。

即使在允许编译器进行优化的“即兴”规则下,可观察到的行为(由于打印到标准输出流)也应该好像至少存在一个共享指针(针对每个项目),直到结束为止。测试的范围。那显然不是你所看到的。

我的有根据的猜测是MSVC会完全优化副本。而是直接在循环体内引用向量内部的指针。单靠那是可以的。

该错误可能是因为它处理了错误的代码,该代码在continue陈述的情况下会破坏局部变量并错误地将其应用于向量中的对象。这是一个错误。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

MSVC编译器可能的错误

来自分类Dev

MSVC中可能的编译器错误

来自分类Dev

这是MSVC ++编译器错误吗?

来自分类Dev

MSVC多行宏编译器错误

来自分类Dev

C++ MSVC/GCC/Clang 编译器错误

来自分类Dev

使用指定的初始化程序的MSVC12(VS2013)中可能的编译器错误

来自分类Dev

Visual Studio 可能是错误的编译器错误

来自分类Dev

这可能是Scala编译器错误吗?

来自分类Dev

编译器错误可能与typedef相关

来自分类Dev

编译器错误可能与typedef相关

来自分类Dev

msvc编译器的native-maven-plugin错误“命令行太长。”

来自分类Dev

使用枚举参数重载模板时出现 MSVC 编译器错误

来自分类Dev

从MSVC14切换到MSVC16导致“编译器的堆空间不足(C1060)”错误

来自分类Dev

隐含编译器错误?

来自分类Dev

Swift编译器错误

来自分类Dev

奇怪的编译器错误

来自分类Dev

很多编译器错误

来自分类Dev

奇怪的编译器错误

来自分类Dev

奇怪的编译器错误

来自分类Dev

Maven编译器错误

来自分类Dev

编译器中的错误?

来自分类Dev

Prisma 编译器错误

来自分类Dev

std::set 插入器不尊重自定义比较器。(可能的编译器错误?)

来自分类Dev

什么是-D编译器标志C ++(clang,GNU,MSVC)

来自分类Dev

什么是-D编译器标志C ++(clang,GNU,MSVC)

来自分类Dev

QT Creator 编译器崩溃(MSVC 2015,Qt 5.8)

来自分类Dev

C编译器错误或程序错误?

来自分类Dev

错误的重载导致编译器错误

来自分类Dev

MSVC-C2668对重载函数的模棱两可的调用-它是编译器错误吗?