본문으로 건너뛰기

Tweaks

8.1 Pass by Value

1) Pass by Value

Pass by Value: A parameter-passing method that creates a separate parameter object from the argument.

For copyable types, an lvalue argument is copied and an rvalue argument can be moved.

2) Pass by Reference

Pass by Reference: A parameter-passing method that binds the parameter directly to the argument without creating a separate parameter object.

It avoids the initial copy or move required by pass by value.

3) Pass-by-Value Strategy

Pass-by-Value Strategy: Taking a parameter by value and then moving it to its final destination.

void addName(std::string newName)
{
names.push_back(std::move(newName));
}

This provides one function that accepts both lvalues and rvalues.

4) Copyable Parameter

Copyable Parameter: A parameter type whose objects support copy operations.

Pass by value should generally be considered only when lvalue arguments can be copied.

5) Cheap-to-Move Type

Cheap-to-Move Type: A type whose move operation has relatively low cost.

Pass by value introduces an additional move compared with common reference-based approaches, so the move should be inexpensive.

6) Always-Copied Parameter

Always-Copied Parameter: A parameter whose value is unconditionally stored or otherwise copied by the function.

If copying is conditional, pass by value may unnecessarily construct a parameter that is never used.

7) Overloading Approach

Overloading Approach: Providing separate overloads for lvalue and rvalue arguments.

void addName(const std::string& name)
{
names.push_back(name);
}

void addName(std::string&& name)
{
names.push_back(std::move(name));
}

It minimizes copy and move operations but requires multiple functions.

8) Universal-Reference Approach

Universal-Reference Approach: Using a universal reference and perfect forwarding to handle both lvalues and rvalues.

template<typename T>
void addName(T&& name)
{
names.push_back(std::forward<T>(name));
}

It can be highly efficient but introduces template complexity and universal-reference limitations.

9) Copy/Move Cost

For a copyable parameter that is eventually stored:

  • Overloading: one copy for lvalues, one move for rvalues.
  • Universal reference: typically one copy for lvalues, one move for rvalues.
  • Pass by value: one copy plus one move for lvalues, two moves for rvalues.

Pass by value therefore generally adds one move operation.

10) Construction vs Assignment

Construction: Creating a new object from the parameter.

Assignment: Replacing the value of an already existing object.

Pass-by-value overhead can be more complicated when the parameter is copied through assignment because existing storage may otherwise be reusable.

11) Move-Only Type

Move-Only Type: A type that supports moving but not copying.

Examples include std::unique_ptr.

Pass by value usually offers little advantage for move-only parameters because an rvalue-reference overload alone can handle them with fewer moves.

12) Object Slicing

Object Slicing: Loss of derived-class information when a derived object is copied into a base-class object by value.

void processWidget(Widget w);

SpecialWidget sw;
processWidget(sw);

The parameter contains only the base-class portion of the original object.

Therefore, pass by value is generally inappropriate for polymorphic base-class parameters.


8.2 Emplacement

1) Insertion

Insertion: Adding an already constructed object to a container.

Common insertion functions include:

  • push_back
  • push_front
  • insert
  • insert_after
std::vector<std::string> values;

values.push_back("xyzzy");

Insertion may require construction of a temporary object when the argument type differs from the container's element type.

2) Emplacement

Emplacement: Constructing a new element directly inside a container from constructor arguments.

values.emplace_back("xyzzy");

It can avoid temporary objects required by insertion.

3) Emplacement Functions

Standard containers provide emplacement counterparts to insertion operations.

  • emplace_back
  • emplace_front
  • emplace
  • emplace_hint
  • emplace_after

4) Perfect Forwarding

Perfect Forwarding: Passing arguments while preserving their original value categories.

Emplacement functions use perfect forwarding to pass arguments directly to the constructor of the container element.

values.emplace_back(50, 'x');

The arguments are forwarded directly to the std::string constructor.

5) Temporary Object

Temporary Object: An unnamed intermediate object created during expression evaluation.

For example:

values.push_back("xyzzy");

may conceptually require:

values.push_back(std::string("xyzzy"));

Emplacement can avoid this intermediate construction and destruction.

6) Construction in Container

Construction in Container: Creating the new object directly in the storage where the container element will reside.

Emplacement is most beneficial when the new value is constructed rather than assigned into an existing element.

7) Node-Based Container

Node-Based Container: A container that stores elements in individually constructed nodes.

Node-based containers usually construct new elements directly, making emplacement particularly suitable.

Examples include:

  • std::list
  • std::set
  • std::map
  • std::unordered_set
  • std::unordered_map

8) Emplacement Efficiency

Emplacement is most likely to outperform insertion when:

  1. The value is constructed in the container rather than assigned.
  2. The argument types differ from the container's element type.
  3. The container is unlikely to reject the new value as a duplicate.

If an object of the container's element type already exists, insertion and emplacement may have essentially the same cost.

9) Duplicate Rejection

Duplicate Rejection: Refusal by a container to insert a value because an equivalent value already exists.

Containers such as std::set and std::map may reject duplicates.

An emplacement operation may construct an object before discovering the duplicate, making the construction unnecessary.

10) Resource Management

Resource-Managing Object: An object that owns and automatically releases a resource.

Examples include:

  • std::unique_ptr
  • std::shared_ptr

Passing a raw resource directly to an emplacement function can weaken exception safety.

ptrs.emplace_back(new Widget, killWidget);

The resource should generally be placed under resource-managing ownership before being passed to the container.

std::shared_ptr<Widget> ptr(
new Widget,
killWidget
);

ptrs.emplace_back(std::move(ptr));

11) Direct Initialization

Direct Initialization: Initialization that directly invokes a constructor.

T object(args);

Emplacement functions use direct initialization.

12) Copy Initialization

Copy Initialization: Initialization using syntax that requires an implicit conversion to the target type.

T object = value;

Insertion functions effectively require the argument to be convertible to the container's element type through permitted implicit conversions.

13) explicit Constructor

explicit Constructor: A constructor that cannot be used for implicit conversions.

Direct initialization may use an explicit constructor, while copy initialization cannot.

Because emplacement performs direct initialization, it may accept arguments that an insertion function rejects.

14) Emplacement Type Safety

Emplacement considers constructors, including explicit constructors, when interpreting its arguments.

Therefore, arguments passed to emplacement functions should be checked carefully because code rejected by insertion may compile with emplacement.