Under the new standard, we can use variadic templates together with forward
to write functions that pass their arguments unchanged to some other function. To illustrate such functions, we’ll add an emplace_back
member to our StrVec
class (§ 13.5, p. 526). The emplace_back
member of the library containers is a variadic member template (§ 16.1.4, p. 673) that uses its arguments to construct an element directly in space managed by the container.
Our version of emplace_back
for StrVec
will also have to be variadic, because string
has a number of constructors that differ in terms of their parameters. Because we’d like to be able to use the string
move constructor, we’ll also need to preserve all the type information about the arguments passed to emplace_back
.
As we’ve seen, preserving type information is a two-step process. First, to preserve type information in the arguments, we must define emplace_back
’s function parameters as rvalue references to a template type parameter (§ 16.2.7, p. 693):
class StrVec {
public:
template <class... Args> void emplace_back(Args&&...);
// remaining members as in § 13.5 (p. 526)
};
The pattern in the expansion of the template parameter pack, &&
, means that each function parameter will be an rvalue reference to its corresponding argument.
Second, we must use forward
to preserve the arguments’ original types when emplace_back
passes those arguments to construct
(§ 16.2.7, p. 694):
template <class... Args>
inline
void StrVec::emplace_back(Args&&... args)
{
chk_n_alloc(); // reallocates the StrVec if necessary
alloc.construct(first_free++, std::forward<Args>(args)...);
}
The body of emplace_back
calls chk_n_alloc
(§ 13.5, p. 526) to ensure that there is enough room for an element and calls construct
to create an element in the first_free
spot. The expansion in the call to construct
:
std::forward<Args>(args)...
expands both the template parameter pack, Args
, and the function parameter pack, args
. This pattern generates elements with the form
std::forward<Ti>(ti)
where Ti represents the type of the ith element in the template parameter pack and ti represents the ith element in the function parameter pack. For example, assuming svec
is a StrVec
, if we call
svec.emplace_back(10, 'c'), // adds cccccccccc as a new last element
the pattern in the call to construct
will expand to
std::forward<int>(10), std::forward<char>(c)
By using forward
in this call, we guarantee that if emplace_back
is called with an rvalue, then construct
will also get an rvalue. For example, in this call:
svec.emplace_back(s1 + s2); // uses the move constructor
the argument to emplace_back
is an rvalue, which is passed to construct
as
std::forward<string>(string("the end"))
The result type from forward<string>
is string&&
, so construct
will be called with an rvalue reference. The construct
function will, in turn, forward this argument to the string
move constructor to build this element.
Exercise 16.58: Write the emplace_back
function for your StrVec
class and for the Vec
class that you wrote for the exercises in § 16.1.2 (p. 668).
Exercise 16.59: Assuming s
is a string
, explain svec.emplace_back(s)
.
Exercise 16.60: Explain how make_shared
(§ 12.1.1, p. 451) works.
Exercise 16.61: Define your own version of make_shared
.