C++ 序列容器 (
vector
、forward_list
、list
和 deque
)
都有一个 assign
方法。 例如,std::vector
有
(引用cppreference.com):
void assign( size_type count, const T& value );
template< class InputIt >
void assign( InputIt first, InputIt last );
void assign( std::initializer_list<T> ilist );
据我所知,这些都具有相同的语义 在使用参数构造的临时对象上使用
operator=
assign
方法。 为什么要有单独的方法来做,例如:
vec.assign({1,2,3});
而不仅仅是:
vec = {1,2,3};
?
同时,C++ 关联容器(例如
std::map
)没有
assign
方法。 为什么不? 什么理由足够令人信服
序列容器有它但关联容器没有?
特别是,我最初猜测为什么序列容器有
assign
是否可以避免在适用的情况下创建临时文件
情况,但同样的推理也适用于关联
容器,对吗? (通过移动分配,创建临时对象是
无论如何,可能微不足道。)
这个问题有一个实际应用:我正在创建一个容器 具有序列容器和关联容器的特征, 并想知道是否有充分的理由提供
assign
方法
它。
特别是,我对为什么序列容器具有分配的最初猜测是它避免在适用的情况下创建临时容器,但相同的推理也适用于关联容器,对吧? (并且通过移动分配,创建临时对象可能微不足道。)
我认为你在这里抓住了两个关键点,但你错过了一些背景和历史。
(吹毛求疵:移动分配的成本是微不足道的,而不是临时创建的成本。)
assign
方法确实避免了创建临时对象,但是将临时对象复制到所需对象的成本是多少?成本与容器的大小成线性关系。将其与构造临时对象的成本进行比较。对于序列容器,建造成本也与尺寸成线性关系。然而,对于关联容器,除了输入已排序的特殊情况之外,构建成本具有对数因子 (N log(N))。
因此对于序列容器来说,
assign
方法可以将成本大约减少一半,这是一个很好的胜利。然而,对于关联容器来说,就渐近行为而言,assign
方法的节省是微不足道的;证明另一个成员函数的复杂性没有什么好处。
此时,您可能试图指出移动比复制便宜,这让我们想到了您的第二点。是的,将临时容器移动到变量中的成本微不足道。从 C++17 开始,它就不再是一个成本了。但是,直到 C++11 才引入移动赋值,而
std::vector::assign
、std::deque::assign
和 std::list::assign
早于 C++11。所以当assign
方法被引入时,这是一个节省。在C++11中,好处大大降低了。在 C++17 中,这种好处消失了。
您可能会注意到,C++11 中引入的
std::array
(与移动语义相同的修订版)没有 assign
方法。我不知道这是否是故意的,但值得注意的是,没有人成功推动添加 assign
方法。当移动语义完成几乎相同的事情(自 C++17 以来完全相同)时,不需要 assign
方法。
我必须承认,相比之下,C++11 中也引入了
std::forward_list
,并且确实有 assign
方法。我将其归因于 std::list
拥有该方法,并且希望使界面相似。这更像是“一直都是这样做的”,而不是满足需求。
我正在创建一个具有序列容器和关联容器特性的容器,并且想知道是否有充分的理由为其提供
方法。assign
跳过它。自 C++17 以来它没有任何好处。