Push_back 与 emplace_back

发布于 2024-10-05 03:59:53 字数 298 浏览 0 评论 0原文

我对 push_backemplace_back 之间的区别有点困惑。

void emplace_back(Type&& _Val);
void push_back(const Type& _Val);
void push_back(Type&& _Val);

由于有一个 push_back 重载采用右值引用,我不太明白 emplace_back 的目的是什么?

I'm a bit confused regarding the difference between push_back and emplace_back.

void emplace_back(Type&& _Val);
void push_back(const Type& _Val);
void push_back(Type&& _Val);

As there is a push_back overload taking a rvalue reference I don't quite see what the purpose of emplace_back becomes?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

有深☉意 2024-10-12 03:59:53

除了访客所说的之外:

MSCV10 提供的函数 void emplace_back(Type&& _Val) 是不合格且冗余的,因为正如您所指出的,它严格等同于 push_back(Type& _Val) 。 &_Val)

emplace_back 的真正 C++0x 形式确实很有用:void emplace_back(Args&&...);

它不采用 value_type,而是采用可变参数列表,因此这意味着您现在可以完美地转发参数并直接将对象构造到容器中,而无需使用临时对象。

这很有用,因为无论 RVO 和移动语义给桌面带来了多少聪明,仍然存在 push_back 可能会进行不必要的复制(或移动)的复杂情况。例如,使用 std::map 的传统 insert() 函数,您必须创建一个临时文件,然后将其复制到 std 中: :pair,然后将其复制到映射中:

std::map<int, Complicated> m;
int anInt = 4;
double aDouble = 5.0;
std::string aString = "C++";
    
// cross your finger so that the optimizer is really good
m.insert(std::make_pair(4, Complicated(anInt, aDouble, aString))); 

// should be easier for the optimizer
m.emplace(4, anInt, aDouble, aString);

那么为什么他们不在 MSVC 中实现正确版本的 emplace_back 呢?实际上,不久前它也困扰着我,所以我在 Visual C++ 博客。以下是 Microsoft Visual C++ 标准库实现的官方维护者 Stephan T Lavavej 的回答。

问:beta 2 emplace 函数现在只是某种占位符吗?

答:如您所知,可变参数模板
VC10 中未实现。我们
用预处理器模拟它们
诸如此类的机械
make_shared()、元组和新的
中的内容。这
预处理机械相对
使用和维护困难。还,
它显着影响编译
速度,因为我们必须反复
包括副标题。由于一个
结合我们的时间限制
和编译速度问题,我们
还没有模拟可变参数模板
在我们的定位功能中。

当可变参数模板是
在编译器中实现,你可以
期望我们能利用
他们在图书馆,包括在
我们的职能。我们采取
非常重视一致性,但是
不幸的是,我们无法做到一切
一次全部完成。

这是一个可以理解的决定。每个尝试过使用预处理器可怕的技巧来模拟可变参数模板的人都知道这东西有多恶心。

In addition to what visitor said :

The function void emplace_back(Type&& _Val) provided by MSCV10 is non-conforming and redundant because as you noted it is strictly equivalent to push_back(Type&& _Val).

But the real C++0x form of emplace_back is really useful: void emplace_back(Args&&...);

Instead of taking a value_type, it takes a variadic list of arguments, so that means that you can now perfectly forward the arguments and construct directly an object into a container without a temporary at all.

That's useful because no matter how much cleverness RVO and move semantics bring to the table, there are still complicated cases where a push_back is likely to make unnecessary copies (or move). For example, with the traditional insert() function of a std::map, you have to create a temporary, which will then be copied into a std::pair<Key, Value>, which will then be copied into the map :

std::map<int, Complicated> m;
int anInt = 4;
double aDouble = 5.0;
std::string aString = "C++";
    
// cross your finger so that the optimizer is really good
m.insert(std::make_pair(4, Complicated(anInt, aDouble, aString))); 

// should be easier for the optimizer
m.emplace(4, anInt, aDouble, aString);

So why didn't they implement the right version of emplace_back in MSVC? Actually, it bugged me too a while ago, so I asked the same question on the Visual C++ blog. Here is the answer from Stephan T Lavavej, the official maintainer of the Visual C++ standard library implementation at Microsoft.

Q: Are beta 2 emplace functions just some kind of placeholder right now?

A: As you may know, variadic templates
aren't implemented in VC10. We
simulate them with preprocessor
machinery for things like
make_shared<T>(), tuple, and the new
things in <functional>. This
preprocessor machinery is relatively
difficult to use and maintain. Also,
it significantly affects compilation
speed, as we have to repeatedly
include subheaders. Due to a
combination of our time constraints
and compilation speed concerns, we
haven't simulated variadic templates
in our emplace functions.

When variadic templates are
implemented in the compiler, you can
expect that we'll take advantage of
them in the libraries, including in
our emplace functions. We take
conformance very seriously, but
unfortunately, we can't do everything
all at once.

It's an understandable decision. Everyone who tried just once to emulate variadic template with preprocessor horrible tricks knows how disgusting this stuff gets.

看春风乍起 2024-10-12 03:59:53

emplace_back 不应采用 vector::value_type 类型的参数,而应采用转发到附加项的构造函数的可变参数。

template <class... Args> void emplace_back(Args&&... args); 

可以传递一个value_type,它将被转发到复制构造函数。

因为它转发参数,这意味着如果您没有右值,这仍然意味着容器将存储“复制的”副本,而不是移动的副本。

 std::vector<std::string> vec;
 vec.emplace_back(std::string("Hello")); // moves
 std::string s;
 vec.emplace_back(s); //copies

但上面的内容应该与 push_back 的作用相同。它可能更适合以下用例:

 std::vector<std::pair<std::string, std::string> > vec;
 vec.emplace_back(std::string("Hello"), std::string("world")); 
 // should end up invoking this constructor:
 //template<class U, class V> pair(U&& x, V&& y);
 //without making any copies of the strings

emplace_back shouldn't take an argument of type vector::value_type, but instead variadic arguments that are forwarded to the constructor of the appended item.

template <class... Args> void emplace_back(Args&&... args); 

It is possible to pass a value_type which will be forwarded to the copy constructor.

Because it forwards the arguments, this means that if you don't have rvalue, this still means that the container will store a "copied" copy, not a moved copy.

 std::vector<std::string> vec;
 vec.emplace_back(std::string("Hello")); // moves
 std::string s;
 vec.emplace_back(s); //copies

But the above should be identical to what push_back does. It is probably rather meant for use cases like:

 std::vector<std::pair<std::string, std::string> > vec;
 vec.emplace_back(std::string("Hello"), std::string("world")); 
 // should end up invoking this constructor:
 //template<class U, class V> pair(U&& x, V&& y);
 //without making any copies of the strings
苏佲洛 2024-10-12 03:59:53

以下示例演示了 emplace_back 的优化。

对于emplace_back,将调用构造函数A (int x_arg)

对于push_back,首先调用A (int x_arg),然后调用move A (A &&rhs)

当然,构造函数必须被标记为显式,但对于当前的示例,最好删除显式。

#include <iostream>
#include <vector>
class A
{
public:
  A (int x_arg) : x (x_arg) { std::cout << "A (x_arg)\n"; }
  A () { x = 0; std::cout << "A ()\n"; }
  A (const A &rhs) noexcept { x = rhs.x; std::cout << "A (A &)\n"; }
  A (A &&rhs) noexcept { x = rhs.x; std::cout << "A (A &&)\n"; }

private:
  int x;
};

int main ()
{
  {
    std::vector<A> a;
    std::cout << "call emplace_back:\n";
    a.emplace_back (0);
  }
  {
    std::vector<A> a;
    std::cout << "call push_back:\n";
    a.push_back (1);
  }
  return 0;
}

输出:

call emplace_back:
A (x_arg)

call push_back:
A (x_arg)
A (A &&)

Optimization for emplace_back can be demonstrated in following example.

For emplace_back, constructor A (int x_arg) will be called.

For push_back, A (int x_arg) is called first and move A (A &&rhs) is called afterward.

Of course, the constructor has to be marked as explicit, but for the current example, it is good to remove explicitness.

#include <iostream>
#include <vector>
class A
{
public:
  A (int x_arg) : x (x_arg) { std::cout << "A (x_arg)\n"; }
  A () { x = 0; std::cout << "A ()\n"; }
  A (const A &rhs) noexcept { x = rhs.x; std::cout << "A (A &)\n"; }
  A (A &&rhs) noexcept { x = rhs.x; std::cout << "A (A &&)\n"; }

private:
  int x;
};

int main ()
{
  {
    std::vector<A> a;
    std::cout << "call emplace_back:\n";
    a.emplace_back (0);
  }
  {
    std::vector<A> a;
    std::cout << "call push_back:\n";
    a.push_back (1);
  }
  return 0;
}

output:

call emplace_back:
A (x_arg)

call push_back:
A (x_arg)
A (A &&)
木緿 2024-10-12 03:59:53

emplace_back 的具体用例:如果您需要创建一个临时对象,然后将其推送到容器中,请使用 emplace_back 而不是 push_back。它将在容器内就地创建对象。

注意:

  1. 在上述情况下 push_back 将创建一个临时对象并移动它
    放入容器中。但是,用于 emplace_back 的就地构造会更有效
    比构造然后移动对象(通常涉及一些复制)更高效。
  2. 一般来说,您可以在所有情况下使用 emplace_back 而不是 push_back ,不会有太大问题。 (请参阅例外

Specific use case for emplace_back: If you need to create a temporary object which will then be pushed into a container, use emplace_back instead of push_back. It will create the object in-place within the container.

Notes:

  1. push_back in the above case will create a temporary object and move it
    into the container. However, in-place construction used for emplace_back would be more
    performant than constructing and then moving the object (which generally involves some copying).
  2. In general, you can use emplace_back instead of push_back in all the cases without much issue. (See exceptions)
掌心的温暖 2024-10-12 03:59:53

列表的另一个示例:

// constructs the elements in place.                                                
emplace_back("element");

// creates a new object and then copies (or moves) that object.
push_back(ExplicitDataType{"element"});

One more example for lists:

// constructs the elements in place.                                                
emplace_back("element");

// creates a new object and then copies (or moves) that object.
push_back(ExplicitDataType{"element"});
猥琐帝 2024-10-12 03:59:53

这里显示了 Push_back 和 emplace_back 的一个很好的代码。

http://en.cppreference.com/w/cpp/container/vector/emplace_back

您可以在push_back上看到移动操作,而不是在emplace_back上看到移动操作。

A nice code for the push_back and emplace_back is shown here.

http://en.cppreference.com/w/cpp/container/vector/emplace_back

You can see the move operation on push_back and not on emplace_back.

你的心境我的脸 2024-10-12 03:59:53

当添加到向量时,符合 emplace_back 的实现会将参数转发给 vector::value_type 构造函数。我记得 Visual Studio 不支持可变参数模板,但 Visual Studio 2013 RC 将支持可变参数模板,所以我猜会添加一个符合要求的签名。

使用 emplace_back,如果将参数直接转发到 vector::value_type 构造函数,则不需要为 emplace_back 可移动或可复制的类型 函数,严格来说。在 vector 情况下,这没有用,因为 vector::value_type 需要可复制或可移动类型来增长。

注意这对于std::map可能很有用,因为一旦在映射中分配了一个条目,就不需要移动它或不再复制,这与 vector 不同,这意味着您可以将 std::map 有效地用于既不可复制也不可移动的映射类型。

emplace_back conforming implementation will forward arguments to the vector<Object>::value_typeconstructor when added to the vector. I recall Visual Studio didn't support variadic templates, but with variadic templates will be supported in Visual Studio 2013 RC, so I guess a conforming signature will be added.

With emplace_back, if you forward the arguments directly to vector<Object>::value_type constructor, you don't need a type to be movable or copyable for emplace_back function, strictly speaking. In the vector<NonCopyableNonMovableObject> case, this is not useful, since vector<Object>::value_type needs a copyable or movable type to grow.

But note that this could be useful for std::map<Key, NonCopyableNonMovableObject>, since once you allocate an entry in the map, it doesn't need to be moved or copied ever anymore, unlike with vector, meaning that you can use std::map effectively with a mapped type that is neither copyable nor movable.

空心空情空意 2024-10-12 03:59:53

emplace_back 函数是 std::vector 容器的一个方法。它构造一个新元素(例如,std::thread 对象)并将其直接附加到向量中。

例如:

#include <iostream>
#include <vector>
#include <thread>
#include <array>
#include <string>

constexpr int num_threads = 4;

// A simple function for each thread to run
void process_data(int thread_id, std::vector<std::string>& data) {
    // Simulate work by adding some string to the vector
    data.push_back("Thread " + std::to_string(thread_id) + " processed data.");
}

int main() {
    // Vector to hold the threads
    std::vector<std::thread> threads;

    // Array of vectors, each vector will store strings for each thread's results
    std::array<std::vector<std::string>, num_threads> lines_array;

    // Create and launch threads
    for (int i = 0; i < num_threads; ++i) {
        // On each iteration of the loop, a new thread is launched to execute the process_data function with its unique thread_id and its corresponding std::vector<std::string> from lines_array.
        // std::ref(lines_array[i]) ensures that each thread modifies its respective vector in the lines_array directly, without copying it
        // All the threads are stored in the threads vector so that they can be managed (e.g., joined) later.
        // emplace_back takes a variadic list of arguments, so that means that you can now perfectly forward the arguments and construct directly an object into a container without a temporary at all.
        threads.emplace_back(process_data, i, std::ref(lines_array[i]));
    }

    // Wait for all threads to complete
    for (auto& thread : threads) {
        thread.join();
    }

    // Print out the results from each thread
    for (int i = 0; i < num_threads; ++i) {
        for (const auto& line : lines_array[i]) {
            std::cout << line << std::endl;
        }
    }

    return 0;
}

The emplace_back function is a method of the std::vector container. It constructs and appends a new element (e.g., a std::thread object) directly to the vector.

For example:

#include <iostream>
#include <vector>
#include <thread>
#include <array>
#include <string>

constexpr int num_threads = 4;

// A simple function for each thread to run
void process_data(int thread_id, std::vector<std::string>& data) {
    // Simulate work by adding some string to the vector
    data.push_back("Thread " + std::to_string(thread_id) + " processed data.");
}

int main() {
    // Vector to hold the threads
    std::vector<std::thread> threads;

    // Array of vectors, each vector will store strings for each thread's results
    std::array<std::vector<std::string>, num_threads> lines_array;

    // Create and launch threads
    for (int i = 0; i < num_threads; ++i) {
        // On each iteration of the loop, a new thread is launched to execute the process_data function with its unique thread_id and its corresponding std::vector<std::string> from lines_array.
        // std::ref(lines_array[i]) ensures that each thread modifies its respective vector in the lines_array directly, without copying it
        // All the threads are stored in the threads vector so that they can be managed (e.g., joined) later.
        // emplace_back takes a variadic list of arguments, so that means that you can now perfectly forward the arguments and construct directly an object into a container without a temporary at all.
        threads.emplace_back(process_data, i, std::ref(lines_array[i]));
    }

    // Wait for all threads to complete
    for (auto& thread : threads) {
        thread.join();
    }

    // Print out the results from each thread
    for (int i = 0; i < num_threads; ++i) {
        for (const auto& line : lines_array[i]) {
            std::cout << line << std::endl;
        }
    }

    return 0;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文