std::initializer_list 中的 std::shared_ptr 似乎过早被破坏

这确实是由 Visual Studio 中的错误引起的 - 它已经得到修复.将更新 2 应用到 Visual Studio 后,该问题无法重现(此处提供候选版本).我道歉;我以为我的补丁是最新的.

<小时>

当我在 Visual Studio 2013 中运行以下代码时,我终生无法弄清楚为什么会出现段错误:

#include #include <内存>结构基{虚拟 int GetValue() { 返回 0;}};struct Derived1 : 公共基础{int GetValue() 覆盖 { 返回 1;}};struct Derived2 : 公共基础{int GetValue() 覆盖 { 返回 2;}};int main(){std::initializer_list>富{std::make_shared(),std::make_shared()};自动迭代器 = std::begin(foo);(*iter)->GetValue();//访问冲突返回0;}

我希望 initializer_list 拥有创建的 shared_ptr 的所有权,将它们保留在范围内,直到 main 结束.>

奇怪的是,如果我尝试访问列表中的第二项,我会得到预期的行为.例如:

 auto iter = std::begin(foo) + 1;(*iter)->GetValue();//返回 2

考虑到这些事情,我猜这可能是编译器中的一个错误 - 但我想确保我没有忽略为什么可能会出现这种行为的一些解释(例如,可能是在initializer_lists).

这种行为是否可以在其他编译器中重现,或者有人可以解释可能发生的情况吗?

解决方案

查看 原始答案 分析对象生命周期问题中的代码.这可以隔离错误.

<小时>

我做了一个最小的复制.这是更多的代码,但涉及的库代码要少得多.而且更容易追踪.

#include 模板结构体{int i = N;typedef X自己;虚拟 int GetValue() { 返回 0;}X() { std::cerr <<X<"<<N<<>() 默认构造函数"<<std::endl;}X(const self& right) : i(right.i) { std::cerr <<X<"<<N<<>(const X<"<<N<<>&)copy-ctor"<<std::endl;}X(self&& right) : i(right.i) { std::cerr <<X<"<<N<<>(X<"<<N<<>&&)移动复制-ctor"<<std::endl;}模板X(const X& right) : i(right.i) { std::cerr <<X<"<<N<<>(const X<"<<M<<>&)转换-ctor"<<std::endl;}模板X(X<M>&& right) : i(right.i) { std::cerr <<X<"<<N<<>(X<"<<M<<>&&)移动转换因子"<<std::endl;}~X() { std::cerr <<~X<"<<N<<">(), i = " <<我<<std::endl;}};模板X Nmake_X() { 返回 X{};}#include int main(){std::initializer_list(),make_X<2>(),make_X<3>(),make_X<4>(),};std::cerr <<已到达主要内容的结尾"<<std::endl;返回0;}

两个 x64 上的输出都是错误的:

C:CodeSO22924358>cl/EHsc minimum.cpp适用于 x64 的 Microsoft (R) C/C++ 优化编译器版本 18.00.21005.1版权所有 (C) 微软公司.版权所有.最小cppMicrosoft (R) 增量链接器版本 12.00.21005.1版权所有 (C) 微软公司.版权所有./out:minimal.exe最小.objC:CodeSO22924358> 最小X<1>() 默认构造函数X 0 (X 1 &&)移动转换因子X<2>() 默认构造函数X 0 (X 2 &&)移动转换因子X<3>() 默认构造函数X 0 (X 3 &&)移动转换因子X<4>() 默认构造函数X 0 (X 4 &&)移动转换因子~X 0 (),i = 2~X 2 (),i=2~X 0 (),i = 1~X 1 (),i=1到达主要结束~X 0 (),i = 4~X 0 (),i = 3~X 0 (),i = 2~X 0 (),i = 1

和 x86:

C:CodeSO22924358>cl/EHsc minimum.cppMicrosoft (R) C/C++ 优化编译器版本 18.00.21005.1 for x86版权所有 (C) 微软公司.版权所有.最小cppMicrosoft (R) 增量链接器版本 12.00.21005.1版权所有 (C) 微软公司.版权所有./out:minimal.exe最小.objC:CodeSO22924358> 最小X<1>() 默认构造函数X 0 (X 1 &&)移动转换因子X<2>() 默认构造函数X 0 (X 2 &&)移动转换因子X<3>() 默认构造函数X 0 (X 3 &&)移动转换因子X<4>() 默认构造函数X 0 (X 4 &&)移动转换因子~X 0 (),i = 2~X 2 (),i=2~X 0 (),i = 1~X 1 (),i=1到达主要结束~X 0 (),i = 4~X 0 (),i = 3~X 0 (),i = 2~X 0 (),i = 1

绝对是一个编译器错误,而且非常严重.如果您在 Connect I 上提交报告,许多其他人会很乐意为您投票.

Edit: This is indeed caused by a bug in Visual Studio - and it has already been fixed. The issue is not reproducible after applying Update 2 to Visual Studio (release candidate available here). I apologize; I thought I was up to date with my patches.


I can't for the life of me figure out why I get a seg fault when I run the following code in Visual Studio 2013:

#include <initializer_list>
#include <memory>

struct Base
{
    virtual int GetValue() { return 0; }
};

struct Derived1 : public Base
{
    int GetValue() override { return 1; }
};

struct Derived2 : public Base
{
    int GetValue() override { return 2; }
};

int main()
{
    std::initializer_list< std::shared_ptr<Base> > foo
        {
            std::make_shared<Derived1>(),
            std::make_shared<Derived2>()
        };

    auto iter = std::begin(foo);
    (*iter)->GetValue(); // access violation

    return 0;
}

I was expecting the initializer_list to take ownership of the created shared_ptrs, keeping them in scope until the end of main.

Oddly enough, if I try to access the second item in the list, I get the expected behavior. For example:

    auto iter = std::begin(foo) + 1;
    (*iter)->GetValue(); // returns 2

Considering these things, I'm guessing this may be a bug in the compiler - but I wanted to make sure I wasn't overlooking some explanation for why this behavior might be expected (e.g., maybe in how rvalues are handled in initializer_lists).

Is this behavior reproducible in other compilers, or can someone explain what might be happening?

解决方案

See the original answer for analysis of object lifetimes of the code in the question. This one isolates the bug.


I made a minimal reproduction. It's more code, but a lot less library code involved. And easier to trace.

#include <initializer_list>

template<size_t N>
struct X
{
    int i = N;

    typedef X<N> self;
    virtual int GetValue() { return 0; }
    X()                               { std::cerr << "X<" << N << ">() default ctor" << std::endl; }
    X(const self& right) : i(right.i) { std::cerr << "X<" << N << ">(const X<" << N << "> &) copy-ctor" << std::endl; }
    X(self&& right)      : i(right.i) { std::cerr << "X<" << N << ">(X<" << N << ">&&      ) moving copy-ctor" << std::endl; }

    template<size_t M>
    X(const X<M>& right) : i(right.i) { std::cerr << "X<" << N << ">(const X<" << M << "> &) conversion-ctor" << std::endl; }
    template<size_t M>
    X(X<M>&& right)      : i(right.i) { std::cerr << "X<" << N << ">(X<" << M << ">&&      ) moving conversion-ctor" << std::endl; }

    ~X() { std::cerr << "~X<" << N << ">(), i = " << i << std::endl; }
};

template<size_t N>
X<N> make_X() { return X<N>{}; }

#include <iostream>
int main()
{
    std::initializer_list< X<0> > foo
        {
            make_X<1>(),
            make_X<2>(),
            make_X<3>(),
            make_X<4>(),
        };

    std::cerr << "Reached end of main" << std::endl;

    return 0;
}

The output is BAD on both x64:

C:CodeSO22924358>cl /EHsc minimal.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 18.00.21005.1 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

minimal.cpp
Microsoft (R) Incremental Linker Version 12.00.21005.1
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:minimal.exe
minimal.obj

C:CodeSO22924358>minimal
X<1>() default ctor
X<0>(X<1>&&      ) moving conversion-ctor
X<2>() default ctor
X<0>(X<2>&&      ) moving conversion-ctor
X<3>() default ctor
X<0>(X<3>&&      ) moving conversion-ctor
X<4>() default ctor
X<0>(X<4>&&      ) moving conversion-ctor
~X<0>(), i = 2
~X<2>(), i = 2
~X<0>(), i = 1
~X<1>(), i = 1
Reached end of main
~X<0>(), i = 4
~X<0>(), i = 3
~X<0>(), i = 2
~X<0>(), i = 1

and x86:

C:CodeSO22924358>cl /EHsc minimal.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 18.00.21005.1 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

minimal.cpp
Microsoft (R) Incremental Linker Version 12.00.21005.1
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:minimal.exe
minimal.obj

C:CodeSO22924358>minimal
X<1>() default ctor
X<0>(X<1>&&      ) moving conversion-ctor
X<2>() default ctor
X<0>(X<2>&&      ) moving conversion-ctor
X<3>() default ctor
X<0>(X<3>&&      ) moving conversion-ctor
X<4>() default ctor
X<0>(X<4>&&      ) moving conversion-ctor
~X<0>(), i = 2
~X<2>(), i = 2
~X<0>(), i = 1
~X<1>(), i = 1
Reached end of main
~X<0>(), i = 4
~X<0>(), i = 3
~X<0>(), i = 2
~X<0>(), i = 1

Definitely a compiler bug, and a pretty severe one. If you file a report on Connect I and many others will be happy to upvote.

相关文章