编译但失败的 C++ std::vector<std::auto_ptr<T>> 示例

时间:2021-02-18 20:26:02

标签: c++ vector unique-ptr auto-ptr

对于某些数据类型 std::vector<std::auto_ptr<T>>std::vector<std::unique_ptr<T>> 可以编译但无法正确执行,而带有 T 的相同程序可以正确编译和运行,这样的简单 C++ 程序是什么?< /p>

我知道 std::auto_ptr 已被弃用或删除;我只想要一个涉及容器的例子来激励为什么它被弃用或删除。

我在 MacOS Big Sur 11.2.1 版上使用 g++-10 -std=c++20

2 个答案:

答案 0 :(得分:6)

std::auto_ptr 根本不能在标准容器中使用。在这种情况下,它不会保持正确的语义。这就是为什么首先在 C++11 中发明移动语义和 std::unique_ptr 的原因之一。 std::auto_ptr 在 C++11 中已弃用,并在 C++17 中完全删除。所以不要在现代编码中使用它。

此处详细说明了 std::auto_ptr 被弃用的官方原因:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1856.html#20.4.5%20-%20Class%20template%20auto_ptr

给出的示例在 std::sort() 上使用 std::vector<std::auto_ptr<int>>

<块引用>

通过这样的设计,可以将 auto_ptr 放入容器中:

vector<auto_ptr<int> > vec;

然而,这种设计的现场经验揭示了一些微妙的问题。即:

sort(vec.begin(), vec.end(), indirect_less());

根据 sort 的实现,上面这行看起来合理的代码可能会也可能不会按预期执行,甚至可能崩溃!问题是 sort 的某些实现会从序列中挑选一个元素,并存储它的本地副本

...
value_type pivot_element = *mid_point;
...

算法假定在此构造之后 pivot_element*mid_point 是等效的。然而,当 value_type 变成 auto_ptr 时,这个假设失败了,随后算法也失败了。

解决此问题的方法是通过禁止从 auto_ptr 进行“复制”,使 const auto_ptr 不适合容器。使用这样的 auto_ptr,如果您尝试将其放入容器中,则会出现编译时错误。

最后的结论是这样的:

<块引用>

调用任何对 std 进行操作的泛型代码,无论是否为 auto_ptr 都是有风险的,因为泛型代码可能会假设一些看起来像复制操作的东西,实际上 em> 复制操作。

结论:

不应使用复制语法从左值移动。应改用其他移动语法。否则通用代码可能会在需要复制时启动移动。

auto_ptr 使用复制语法从左值移出,因此从根本上是不安全的。

答案 1 :(得分:0)

<script src="https://code.jquery.com/jquery-3.5.1.js"></script> <select id="race" name="races" class="select2 select2-hidden-accessible" multiple="" data-select2-id="race" tabindex="-1" aria-hidden="true"> <option value="RACE_AI_AN" data-select2-id="41">American Indian or Alaska Native</option> <option value="RACE_ASIAN" data-select2-id="42">Asian</option> <option value="RACE_BL_AA" data-select2-id="43">Black or African American</option> <option value="RACE_NH_PI" data-select2-id="44">Native Hawaiian or Other Pacific Islander</option> <option value="RACE_WHITE" data-select2-id="45">White</option> </select> <br> <select id="gender" name="gender" class="select2 select2-hidden-accessible" data-select2-id="gender" tabindex="-1" aria-hidden="true"> <option value="" data-select2-id="28">Gender</option> <option value="GENDER_MALE">Male</option> <option value="GENDER_FEMALE">Female</option> <option value="GENDER_OTHER">Another Designation</option> </select> 的问题在于它可以被复制并且复制会修改原始:

auto_ptr

这与移动现在所做的类似,只是在 a = b; // transfers ownership from b to a 时,语言中没有移动语义。现在 C++ 有了移动语义的所有权转移可以更清楚地表达:

auto_ptr

没有人会/不应该期望该行中的 a = std::move(b); 不会被修改。

然而,对于 b,通常假设 a = b 不会被修改。 b 打破了这个假设。考虑例如:

auto_ptr

使用 template <typename P> void foo() { std::vector<P> x; x.resize(42); int i=0; for (auto& e : x) e.reset(new int(i++)); for (auto e : x) { std::cout << *e << "\n"; } for (auto e : x) { std::cout << *e << "\n"; } } 这将导致 a compiler error

P=std::unique_ptr<int>

虽然它使用 <source>:17:15: error: call to deleted constructor of 'std::unique_ptr<int, std::default_delete<int> >' for (auto e : x) { ^ ~ 编译但是是未定义的行为(例如这里的段错误:https://godbolt.org/z/93hdse),因为它取消引用空指针。

任何算法的类似问题都假定复制元素“可以”。例如,按值接受参数的 P=std::auto_ptr<int> 比较器确实可以编译但会造成严重破坏:

auto_ptr

复制的过程并不总是那么明显,当元素可复制时,算法可能会在内部复制元素。

相关问题