string_view
operator+
vs. StrCat()
absl::Status
std::bind
absl::optional
and std::unique_ptr
absl::StrFormat()
make_unique
and private
Constructors.bool
explicit
= delete
)switch
Statements Responsibly= delete
AbslHashValue
and Youcontains()
std::optional
parametersif
and switch
statements with initializersinline
Variablesstd::unique_ptr
Must Be MovedAbslStringify()
vector.at()
auto
for Variable DeclarationsOriginally posted as totw/65 on 2013-12-12
By Hyrum Wright ([email protected])
“Let me ’splain. No, there is too much. Let me sum up.” –Inigo Montoya
C++11 added a new way to insert elements into standard containers: the
emplace()
family of methods. These methods create an object directly within a
container, instead of creating a temporary object and then copying or moving
that object into the container. Avoiding these copies is more efficient for
almost all objects, and makes it easier to store move-only objects (such as
std::unique_ptr
) in standard containers.
Let’s look at a simple example using vectors to contrast the two styles. The first example uses pre-C++11 code:
class Foo {
public:
Foo(int x, int y);
…
};
void addFoo() {
std::vector<Foo> v1;
v1.push_back(Foo(1, 2));
}
Using the older push_back()
method, two Foo
objects are constructed: the
temporary argument and the object in the vector that is move-constructed from
the temporary.
We can instead use C++11’s emplace_back()
and only one object will be
constructed directly within the memory of the vector. Since the “emplace” family
of functions forward their arguments to the underlying object’s constructor, we
can provide the constructor arguments directly, obviating the need to create a
temporary Foo
:
void addBetterFoo() {
std::vector<Foo> v2;
v2.emplace_back(1, 2);
}
So far, we’ve looked at cases where emplace methods improve performance, but
they also make previously impossible code feasible, such as storing move-only
types like std::unique_ptr
within containers. Consider this snippet:
std::vector<std::unique_ptr<Foo>> v1;
How would you insert values into this vector? One way would be to use
push_back()
and construct the value directly within its argument:
v1.push_back(std::unique_ptr<Foo>(new Foo(1, 2)));
This syntax works, but can be a bit unwieldy. Unfortunately, the traditional way of getting around this confusion is fraught with complexity:
Foo *f2 = new Foo(1, 2);
v1.push_back(std::unique_ptr<Foo>(f2));
This code compiles, but it leaves ownership of the raw pointer unclear until the
insertion. What’s worse, the vector now owns the object, but f2
still remains
valid, and could accidentally be deleted later on. To an uninformed reader, this
ownership pattern can be confusing, particularly if construction and insertion
are not sequential events as above.
Other solutions won’t even compile, because unique_ptr
isn’t copyable:
std::unique_ptr<Foo> f(new Foo(1, 2));
v1.push_back(f); // Does not compile!
v1.push_back(new Foo(1, 2)); // Does not compile!
Using emplace methods can make it more intuitive to insert the object while it’s
being created. In other cases, if you need to move the unique_ptr
into the
vector, you can:
std::unique_ptr<Foo> f(new Foo(1, 2));
v1.emplace_back(new Foo(1, 2));
v1.push_back(std::move(f));
By combining emplace with a standard iterator, you can also insert the object at an arbitrary location in the vector:
v1.emplace(v1.begin(), new Foo(1, 2));
That said, in practical terms we wouldn’t want to see these ways to construct a
unique_ptr
- Use std::make_unique
(from C++14) or absl::make_unique
(if
you’re still on C++11).
We’ve used vector as an example in this Tip, but emplace methods are also
available for maps, lists and other STL containers. When combined with
unique_ptr
, emplace allows for good encapsulation and makes the ownership
semantics of heap-allocated objects clear in ways that weren’t possible before.
Hopefully this has given you a feel for the power of the new emplace family of
container methods, and a desire to use them where appropriate in your own code.