为什么C ++ 11从std :: vector的fill构造函数的原型中删除了默认值?

时间:2017-08-28 19:44:24

标签: c++ c++11 vector

在C ++ 98中,std::vector的填充构造函数的原型具有初始化程序的默认值。

explicit vector (size_type n, const value_type& val = value_type(),
                 const allocator_type& alloc = allocator_type());

C ++ 11使用两个原型。

explicit vector (size_type n);
         vector (size_type n, const value_type& val,
                 const allocator_type& alloc = allocator_type());

(在C ++ 14中,填充构造函数再次改变,但这不是这个问题的重点。)

参考链接为here

为什么C ++ 11会弃用默认的初始化值value_type()

顺便说一句,我尝试使用clang++ -std=c++11编译以下代码并发出错误,这意味着值类型仍然需要具有S() {}之类的默认构造函数,即默认构造

#include <vector>

struct S {
    int k;
    S(int k) : k(k) {} // intentionally remove the synthesized default constructor
};

int main() {
    std::vector<S> s(5); // error: no matching constructor
}

2 个答案:

答案 0 :(得分:52)

C ++ 98采用原型对象,然后复制了n次。默认情况下,原型是默认构造的对象。

C ++ 11版本构造了n个默认构造的对象。

这消除了n个副本并用n个default-constructions替换它。此外,它避免了构建原型。

假设您的课程如下:

struct bulky {
  std::vector<int> v;
  bulky():v(1000) {} // 1000 ints
  bulky(bulky const&)=default;
  bulky& operator=(bulky const&)=default;

  // in C++11, avoid ever having an empty vector to maintain
  // invariants:
  bulky(bulky&& o):bulky() {
    std::swap(v, o.v);
  }
  bulky& operator=(bulky&& o) {
    std::swap(v,o.v);
    return *this;
  }
};

这是一个总是拥有1000 int s缓冲区的类。

如果我们然后创建bulky

的向量
std::vector<bulky> v(2);

在C ++ 98中,这分配了3次1000个整数。在C ++ 11中,这只分配了1000次整数。

此外,C ++ 98版本要求该类型可以复制。 C ++ 11中有不可复制的类型,例如std::unique_ptr<T>,并且使用C ++ 98签名无法生成vector默认构造的唯一指针。 C ++ 11签名没有任何问题。

std::vector<std::unique_ptr<int>> v(100);

如果我们仍然使用C ++ 98版本,则上述操作无效。

答案 1 :(得分:47)

构造函数分为两部分的原因是支持“仅移动”类型,例如unique_ptr<T>

这个构造函数:

vector(size_type n, const T& value, const Allocator& = Allocator());

要求T具有可复制性,因为必须从n复制T value以填充vector

这个构造函数:

explicit vector(size_type n, const Allocator& = Allocator());

要求T是可复制构造的,只能默认构造。

后一个构造函数与unique_ptr<T>

一起使用
std::vector<std::unique_ptr<int>> s(5);

而前一个构造函数没有。

以下是进行此更改的提案:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1858.html#23.2.4.1%20-%20vector%20constructors,%20copy,%20and%20assignment

这篇论文有一些基本原理,但无疑是简洁的一面:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1771.html

Fwiw,resize

void resize(size_type sz, T c = T());

分为:

void resize(size_type sz);
void resize(size_type sz, const T& c);

出于同样的原因。第一个需要默认可构造但不能复制构造(支持默认的可构造移动类型),第二个需要复制可构造。

这些更改并非100%向后兼容。对于某些类型(例如,引用计数的智能指针),从默认构造对象的复制构造与默认构造不同。然而,支持仅移动类型的好处被认为是值得这个API破坏的成本。