std::vector,默认构造,c++ 11和破坏性变化

std::vector, default construction, C++11 and breaking changes

本文关键字:破坏性 c++ 变化 vector 默认 std      更新时间:2023-10-16

我今天遇到了一个很微妙的问题,我想听听你的意见。

考虑下面这个普通的共享体成语类:

struct S
{
    S() : p_impl(new impl) {}
private:
    struct impl;
    boost::shared_ptr<impl> p_impl;
};

当你试着用下面的方式把它们变成向量时,有趣的事情就出现了:

std::vector<S> v(42);

现在,至少对于MSVC 8, v中的所有元素共享同一个impl成员。实际上,导致这个的是vector构造函数:

template <typename T, typename A = ...>
class vector
{
    vector(size_t n, const T& x = T(), const A& a = A());
    ...
};

在场景下,只有一个S对象被默认构造,vectorn元素从它复制。

现在,在c++ 11中,有右值引用。所以它不能这样工作。如果vector被构造为

std::vector<S> v(42);

则很可能,实现将选择在vector内部默认构造n对象,因为复制构造可能不可用。在这种情况下,这将是一个突破性的变化。

我的问题是:
  1. c++ 03标准是否强制要求std::vector必须有一个如上定义的构造函数?使用默认参数?特别是,是否有一个保证,向量对象的条目被复制,而不是默认构造?
  2. c++ 11标准对这一点有什么规定?
  3. 我认为这可能是c++ 03和c++ 11之间的突破性变化。这个问题调查过了吗?解决?

PS:请不要对上面的类S的默认构造函数进行注释。

c++ 03标准是否要求std::vector必须有一个如上定义的构造函数,即带有默认实参?特别是有一个保证,向量对象的条目得到复制,而不是默认构造?

是的,指定的行为是将x复制n次,以便容器初始化为包含n元素,这些元素都是x的副本。


c++ 11标准对这一点有什么规定?

在c++ 11中,这个构造函数变成了两个构造函数。

vector(size_type n, const T& x, const Allocator& = Allocator()); // (1)
explicit vector(size_type n);                                    // (2)
(1)的工作方式与c++ 03中的相同:x被复制n次。

代替x的默认参数,添加了(2)。这个构造函数对容器中的n元素进行值初始化。不复制。

如果需要旧的行为,可以通过向构造函数调用提供第二个参数来确保调用(1):
std::vector<S> v(42, S());

我认为这可能是c++ 03和c++ 11之间的一个重大变化。我认为这可能是c++ 03和c++ 11之间的一个重大变化。这个问题调查过了吗?解决了吗?

是的,正如你的例子所示,这确实是一个突破性的变化。

由于我不是c++标准化委员会的成员(我也没有特别关注邮件中与库相关的论文),我不知道这个突破性的变化讨论到了什么程度。

我认为您描述的用例解决方案不是最佳的,也不是完整的,这就是为什么您在升级到c++ 11时遇到问题的原因。

c++总是关心语义,当你用c++写程序时,你最好理解你的语义。因此,在您的情况下,您希望创建N个对象,但在不更改它们的同时,您希望它们共享相同的内存以进行优化。好主意,但如何做到这一点:复制构造函数。2)静态实现+复制构造函数。你考虑过这两种解决方案吗?

考虑您需要N个对象的M个向量,如果您选择第一种方案,将分配多少次共享内存?它是M,但是如果我们想要创建包含MxN个对象的向量,为什么我们需要分配内存M次?

所以这里的正确实现是默认指向静态内存,并且只在对象改变时才分配内存。在这种情况下,分配N个对象的M个向量会给你…"共享"内存分配。

在你的例子中,你违反了正确的语义滥用复制构造函数,这是:1)不明显2)不优现在你要付出代价了