emplace_back、push_back杂谈

emplace_back

相当于是直接初始化,根据函数重载找最佳匹配的构造函数。若emplace的是一个完整的对象,则根据函数匹配规则,会自动调用拷贝构造函数。

push_back

相当于是拷贝初始化(但是编译器不会进行优化),若push的对象不是完整的对象,则会进行类型转换,转换之后进行添加,添加时会调用拷贝构造函数。

push_back有右值引用的版本,添加的对象又是一个右值,此时会调用右值版本的移动构造。速度也会很快。

性能差别

  • 当插入的都是临时对象时,性能没有差别。
    • 如果有移动构造函数,两者都会先调用构造函数,后调用移动构造函数,性能没有差别。
    • 没有,两者都会先调用构造函数,后调用拷贝构造函数,性能没有差别。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <vector>
#include <string>
#include <iostream>

struct President
{
std::string name;
std::string country;
int year;

President(std::string p_name, std::string p_country, int p_year)
: name(std::move(p_name)), country(std::move(p_country)), year(p_year)
{
std::cout << "I am being constructed.\n";
}
President(President&& other)
: name(std::move(other.name)), country(std::move(other.country)), year(other.year)
{
std::cout << "I am being moved.\n";
}
President(const President& other)
: name(other.name), country(other.country), year(other.year)
{
std::cout << "I am copy construced.\n";
}
President& operator=(const President& other) = default;
};

int main()
{
std::vector<President> elections;
std::cout << "emplace_back:\n";
elections.emplace_back(President("Nelson Mandela", "South Africa", 1994));

std::vector<President> reElections;
std::cout << "\npush_back:\n";
reElections.push_back(President("Franklin Delano Roosevelt", "the USA", 1936));

std::cout << "\nContents:\n";
for (President const& president: elections) {
std::cout << president.name << " was elected president of "
<< president.country << " in " << president.year << ".\n";
}
for (President const& president: reElections) {
std::cout << president.name << " was re-elected president of "
<< president.country << " in " << president.year << ".\n";
}
}

运行结果

1
2
3
4
5
6
7
8
9
10
11
emplace_back:
I am being constructed.
I am being moved.

push_back:
I am being constructed.
I am being moved.

Contents:
Nelson Mandela was elected president of South Africa in 1994.
Franklin Delano Roosevelt was re-elected president of the USA in 1936.
  • 当插入的时完整的对象时,性能没有差距。

    • 因为push_back()是直接调用拷贝构造函数,emplace_back是按照函数匹配规则调用拷贝构造函数,两者性能相同。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    int main()
    {
    std::vector<President> elections;
    President temp("Franklin Delano Roosevelt", "the USA", 1936);
    std::cout << "emplace_back:\n";
    elections.emplace_back(temp);

    std::vector<President> reElections;
    std::cout << "\npush_back:\n";
    reElections.push_back(temp);

    std::cout << "\nContents:\n";
    for (President const& president: elections) {
    std::cout << president.name << " was elected president of "
    << president.country << " in " << president.year << ".\n";
    }
    for (President const& president: reElections) {
    std::cout << president.name << " was re-elected president of "
    << president.country << " in " << president.year << ".\n";
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    I am being constructed.
    emplace_back:
    I am being copy constructed.

    push_back:
    I am being copy constructed.

    Contents:
    Franklin Delano Roosevelt was elected president of the USA in 1936.
    Franklin Delano Roosevelt was re-elected president of the USA in 1936.
  • push_back一个临时对象,emplace_back不是对象时,例如

    1
    2
    3
    4
    5
    6
    7
    std::vector<President> elections;
    std::cout << "emplace_back:\n";
    elections.emplace_back("Franklin Delano Roosevelt", "the USA", 1936);

    std::vector<President> reElections;
    std::cout << "\npush_back:\n";
    reElections.push_back(President("Franklin Delano Roosevelt", "the USA", 1936));
    • 如果对象有移动构造函数,两者性能差距不大,emplace_back会快一点。

      push_back会调用一次构造函数。

      emplace_back会调用一次构造函数,再调用一次移动构造函数,会慢一点。

    • 如果对象没有移动构造函数,emplace_back会快很多。

      push_back会调用一次构造函数。

      emplace_back会调用一次构造函数,再调用一次拷贝构造函数,性能差距比较大。

push_back的类别的拷贝构造函数参数如果不是const会出现错误

push_back()有四种形式。

1
2
3
4
void push_back( const T& value );
constexpr void push_back( const T& value );
void push_back( T&& value );
constexpr void push_back( T&& value );

对于非右值时,把对象转换成const,之后会调用拷贝构造函数,当拷贝构造函数不加const时,会尝试把const对象转换为非const对象,引起错误。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!