std::shared_ptr 中的 std::initializer_list 似乎过早地被摧毁了
std::shared_ptr in an std::initializer_list appears to be getting destroyed prematurely
>编辑:这确实是由Visual Studio中的一个错误引起的 - 并且已经修复。将更新 2 应用于 Visual Studio 后,此问题不可重现(此处提供候选版本)。我道歉;我以为我已经更新了我的补丁。
我一辈子都无法弄清楚为什么当我在Visual Studio 2013中运行以下代码时会出现seg错误:
#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;
}
我期待initializer_list
获得创建的shared_ptr
的所有权,将它们保持在范围内,直到main
年底。
奇怪的是,如果我尝试访问列表中的第二项,我会得到预期的行为。例如:
auto iter = std::begin(foo) + 1;
(*iter)->GetValue(); // returns 2
考虑到这些事情,我猜这可能是编译器中的一个错误 - 但我想确保我没有忽略为什么这种行为可能被预期的一些解释(例如,也许在 initializer_list
s 中如何处理右值)。
这种行为是否可以在其他编译器中重现,或者有人可以解释可能发生的事情吗?
有关分析问题中代码的对象生存期,请参阅原始答案。 这个隔离了错误。
我做了一个最小的复制品。 它更多的代码,但涉及的库代码要少得多。 而且更容易追踪。
#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;
}
两个 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
和 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
绝对是一个编译器错误,而且是一个非常严重的错误。 如果您提交有关Connect I的报告,许多其他人将很乐意投票。
从make_shared
返回的shared_ptr
对象是临时的。 它们将在用于初始化shared_ptr<Base>
实例后,在完整表达式结束时销毁。
但是用户对象(Derived1
和Derived2
)的所有权应该共享(或"转移",如果你愿意)到列表中的shared_ptr
实例。 这些用户对象应一直存在到 main
结束。
我刚刚使用Visual Studio 2013运行了您问题中的代码,并且没有访问冲突。 奇怪的是,当我跟踪到main()
和~Base()
时,我得到以下输出:
C:CodeSO22924358>cl /EHsc main.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 18.00.21005.1 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
main.cpp
Microsoft (R) Incremental Linker Version 12.00.21005.1
Copyright (C) Microsoft Corporation. All rights reserved.
/out:main.exe
main.obj
C:CodeSO22924358>main
~Base()
Reached end of main
~Base()
这看起来确实不对。
如果我用 GetValue()
的返回值做某事,它是错误的(0
而不是1
),我得到了访问冲突。 但是,它会在所有跟踪输出之后发生。 而且似乎有些间歇性。
C:CodeSO22924358>cl /Zi /EHsc main.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 18.00.21005.1 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
main.cpp
Microsoft (R) Incremental Linker Version 12.00.21005.1
Copyright (C) Microsoft Corporation. All rights reserved.
/out:main.exe
/debug
main.obj
C:CodeSO22924358>main
~Base()
GetValue() returns 0
Reached end of main
~Base()
这是我正在使用的代码的最终版本:
#include <initializer_list>
#include <memory>
#include <iostream>
struct Base
{
virtual int GetValue() { return 0; }
~Base() { std::cerr << "~Base()" << std::endl; }
};
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);
std::cerr << "GetValue() returns " << (*iter)->GetValue() << std::endl; // access violation
std::cerr << "Reached end of main" << std::endl;
return 0;
}
单步显示析构函数是在shared_ptr<Derived1>
的初始值设定项列表构造之后立即调用的(正确,它的对象已移动到shared_ptr<Base>
),并且匹配shared_ptr<Base>
,这是非常非常错误的。
- 使用std::multimap迭代器创建std::list
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 如何导出包含具有"std::unique_ptr"值的"std::map"属性的
- 从持续时间构造std::chrono::system_clock::time_point
- std::具有相同基类的类的变体
- std::向量与传递值的动态数组
- 使用std::vector的OpenCL矩阵乘法
- std::map<struct,struct>::find 找不到匹配项,但是如果我循环通过 begin() 到 end(),我在那里看到匹配项
- std::condition_variable::wait()如何评估给定的谓词
- 如何获取std::result_of函数的返回类型
- std::原子加载和存储都需要吗
- 将对象移动到std::shared_ptr
- POCO::PostgreSQL:如何将std::vector支持添加到`Binder::bind`
- 使用一个考虑到std::map中键值的滚动或换行的键
- 如何从 std::atomic 中提取指针 T<T>?
- 为什么 std::unique 不调用 std::sort?
- 使用std::函数映射对象方法
- std::shared_ptr 中的 std::initializer_list 似乎过早地被摧毁了