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

Lee*_*hai 43 c++ vector c++11

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

explicit vector (size_type n, const value_type& val = value_type(),
                 const allocator_type& alloc = allocator_type());
Run Code Online (Sandbox Code Playgroud)

C++ 11使用两个原型.

explicit vector (size_type n);
         vector (size_type n, const value_type& val,
                 const allocator_type& alloc = allocator_type());
Run Code Online (Sandbox Code Playgroud)

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

参考链接在这里.

为什么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
}
Run Code Online (Sandbox Code Playgroud)

Yak*_*ont 51

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;
  }
};
Run Code Online (Sandbox Code Playgroud)

这是一个总是拥有1000 ints 缓冲区的类.

如果我们然后创建一个矢量bulky:

std::vector<bulky> v(2);
Run Code Online (Sandbox Code Playgroud)

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

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

std::vector<std::unique_ptr<int>> v(100);
Run Code Online (Sandbox Code Playgroud)

如果我们还有C++ 98版本,上面的代码将不起作用.

  • 我可能会回复这个解释,因为正确性胜过效率:D但是,这个好问题的答案很好. (2认同)

How*_*ant 47

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

这个构造函数:

vector(size_type n, const T& value, const Allocator& = Allocator());
Run Code Online (Sandbox Code Playgroud)

需要T复制可构造,因为n T必须复制s才能value填充vector.

这个构造函数:

explicit vector(size_type n, const Allocator& = Allocator());
Run Code Online (Sandbox Code Playgroud)

要求T是可复制构造,只有缺省构造.

后一个构造函数适用于unique_ptr<T>:

std::vector<std::unique_ptr<int>> s(5);
Run Code Online (Sandbox Code Playgroud)

而前一个构造函数没有.

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

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

FWIW, resize:

void resize(size_type sz, T c = T());
Run Code Online (Sandbox Code Playgroud)

被分成:

void resize(size_type sz);
void resize(size_type sz, const T& c);
Run Code Online (Sandbox Code Playgroud)

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

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

  • @Mehrdad:那可能已经完成了.我判断`container <T>(n)`的C++ 11行为实际上通常更可取,如果实际上需要C++ 98行为,那么仍然可以使用语法`container <T>( n,T {})`.在绝大多数情况下,没有语义差异(大多数用例不会发生变化).存在一些风险,但有更大的好处.事后看来,我认为赌博有效. (4认同)
  • 这似乎是真正的答案,因为它解释了*为什么*它已经完成.(Yakk目前接受的答案也提到了这一点,但暂时不谈.) (3认同)