push_back vs emplace_back

发布于 2025-01-24 15:09:52 字数 304 浏览 0 评论 0原文

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

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

由于有一个push_back超载进行RVALUE参考,因此我不太了解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

晒暮凉 2025-01-31 15:09:52

除了访客所说的内容外,

MSCV10提供的函数void emplace_back(type& amp; amp; _val)是不合格和多余的,因为正如您所指出的那样,它严格等同于push_back_back(type& amp; amp; & _val)

但是emplace_back的真实C ++ 0x形式确实有用: void emplace_back(args&& ...) ;

它没有采用value_type,而是获取参数的变化列表,因此这意味着您现在可以完美地转发参数并直接将对象直接构造到一个没有临时的容器中。

这很有用,因为不管RVO和移动语义带来了多少聪明,仍然存在复杂的情况,push_back可能会制作不必要的副本(或移动)。例如,使用传统insert() std :: map的功能,您必须创建一个临时性,然后将其复制到std: :pair< key,value>,然后将其复制到地图中:

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的答案。

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

a:您可能知道,variadic模板
在VC10中未实施。我们
用预处理器模拟它们
诸如此类的机械
make_shared&lt; t&gt;(),元组和新的
&lt; function&gt;中的事物。这
预处理器机械相对
难以使用和维护。还,
它显着影响汇编
速度,因为我们必须反复
包括子标题。由于一个
我们的时间限制的结合
以及汇编速度的问题,我们
尚未模拟变异模板
在我们的emplace功能中。

当变异模板为
在编译器中实施,您可以
希望我们会利用
他们在图书馆中,包括
我们的设备功能。我们接受
非常认真地一致,但是
不幸的是,我们不能做所有事情
一次。

这是一个可以理解的决定。每个尝试使用预处理程序可怕技巧模拟variadic模板的人都知道这些东西有多令人作呕。

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.

情绪失控 2025-01-31 15:09:52

emplace_back不应采用类型vector :: value_type的参数,而应转发给附录项目的构造函数。

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

可以传递value_type将转发给复制构造函数。

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

 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
冷了相思 2025-01-31 15:09:52

emplace_back的优化可以在以下示例中进行演示。

对于emplace_back,将调用constructor a(int x_arg)

对于push_backa(int x_arg)被称为move a(a&amp;&amp; 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 &&)
GRAY°灰色天空 2025-01-31 15:09:52

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)
若水般的淡然安静女子 2025-01-31 15:09:52

列表的另一个示例:

// 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"});
淡忘如思 2025-01-31 15:09:52

此处显示了推送_back和emplace_back的一个不错的代码。

http://en.cppreference.com/w/cpp/container/container/container/vector/vector/emplace/emplace/place_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.

鸩远一方 2025-01-31 15:09:52

emplace_back符合实现将将参数转发到vector&lt; object&gt; :: value_type constructor时添加到向量时。我记得Visual Studio不支持Variadic模板,但是在Visual Studio 2013 RC中将支持Variadic模板,因此我想将添加一个符合符号的签名。

使用emplace_back,如果您将参数直接转发到vector&lt; object&gt; :: value_type constructor,则不需要类型可移动或可复制的emplace_back功能,严格来说。在vector&lt; noncopyablenonMovableObject&gt;案例中,这无用,因为vector&lt; object&gt; :: value_type需要可复制或可移动的类型才能成长。

但是 Note 这对于std :: map&lt; key,noncopyablenonMovableObject&gt;,因为一旦您分配了地图中的条目,就无需移动它,因此与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.

玩物 2025-01-31 15:09:52

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

例如:

#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 和您的相关数据。
原文