C++11coreintermediate
Perfect Forwarding (C++11 Addition)
C++11 · Modern Revolution · Compiler flag: -std=c++11
Explanation
A technique using universal references and `std::forward` to pass arguments to another function while preserving their original value category (lvalue or rvalue). This is essential for writing generic wrapper functions.
Real-World Scenario
Build a factory function that constructs objects by forwarding constructor arguments exactly as the caller provided them.
Quick Snippet
snippet.cpp
template<typename T, typename... Args>
std::unique_ptr<T> make(Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}Full Example
perfect_forwarding.cpp
// CodeNebula - C++11 Example: Perfect Forwarding
// Compile: g++ -std=c++11 -Wall -Wextra -o example perfect_forwarding.cpp
// Demonstrates perfect forwarding with std::forward and universal references.
// Perfect forwarding preserves the value category (lvalue/rvalue) of arguments
// through template layers, enabling efficient factory functions and wrappers.
#include <iostream>
#include <string>
#include <utility>
#include <memory>
// Target class that distinguishes how it receives arguments
class Widget {
std::string name_;
public:
Widget(const std::string& name) : name_(name)
{
std::cout << " Widget(const string&): copy \"" << name_ << "\"" << std::endl;
}
Widget(std::string&& name) : name_(std::move(name))
{
std::cout << " Widget(string&&): move \"" << name_ << "\"" << std::endl;
}
const std::string& name() const { return name_; }
};
// ------------------------------------------------------------------
// 1. Without forwarding: always copies
// ------------------------------------------------------------------
template <typename T>
Widget makeWidgetBad(T arg)
{
return Widget(arg); // always an lvalue, never moves
}
// ------------------------------------------------------------------
// 2. With perfect forwarding: preserves value category
// ------------------------------------------------------------------
template <typename T>
Widget makeWidgetGood(T&& arg) // universal reference (T&&)
{
return Widget(std::forward<T>(arg)); // forwards as lvalue or rvalue
}
// ------------------------------------------------------------------
// 3. Variadic perfect forwarding (emplace-style factory)
// ------------------------------------------------------------------
template <typename T, typename... Args>
std::unique_ptr<T> makeUnique(Args&&... args)
{
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
// ------------------------------------------------------------------
// 4. Wrapper that logs and forwards
// ------------------------------------------------------------------
void processImpl(const std::string& s)
{
std::cout << " processImpl(const&): \"" << s << "\"" << std::endl;
}
void processImpl(std::string&& s)
{
std::cout << " processImpl(&&): \"" << s << "\"" << std::endl;
}
template <typename T>
void logAndProcess(T&& arg)
{
std::cout << " [LOG] Processing argument..." << std::endl;
processImpl(std::forward<T>(arg)); // preserve value category
}
int main()
{
// ------------------------------------------------------------------
// Without forwarding (always copies)
// ------------------------------------------------------------------
std::cout << "=== Without forwarding ===" << std::endl;
std::string name = "Alice";
makeWidgetBad(name); // copy (expected)
makeWidgetBad(std::string("Bob")); // still copy (bad!)
std::cout << std::endl;
// ------------------------------------------------------------------
// With perfect forwarding
// ------------------------------------------------------------------
std::cout << "=== With perfect forwarding ===" << std::endl;
makeWidgetGood(name); // forwards as lvalue -> copy
makeWidgetGood(std::string("Bob")); // forwards as rvalue -> move
makeWidgetGood("Charlie"); // forwards const char* -> copy ctor
std::cout << std::endl;
// ------------------------------------------------------------------
// Variadic forwarding (like std::make_unique)
// ------------------------------------------------------------------
std::cout << "=== Variadic forwarding ===" << std::endl;
auto w1 = makeUnique<Widget>(name);
auto w2 = makeUnique<Widget>(std::string("Eve"));
std::cout << " w1->name(): " << w1->name() << std::endl;
std::cout << " w2->name(): " << w2->name() << std::endl;
std::cout << std::endl;
// ------------------------------------------------------------------
// Logging wrapper with forwarding
// ------------------------------------------------------------------
std::cout << "=== Log-and-forward wrapper ===" << std::endl;
std::string msg = "lvalue message";
logAndProcess(msg); // lvalue -> const&
logAndProcess(std::string("rvalue msg")); // rvalue -> &&
return 0;
}
/*
Expected output:
=== Without forwarding ===
Widget(const string&): copy "Alice"
Widget(const string&): copy "Bob"
=== With perfect forwarding ===
Widget(const string&): copy "Alice"
Widget(string&&): move "Bob"
Widget(const string&): copy "Charlie"
=== Variadic forwarding ===
Widget(const string&): copy "Alice"
Widget(string&&): move "Eve"
w1->name(): Alice
w2->name(): Eve
=== Log-and-forward wrapper ===
[LOG] Processing argument...
processImpl(const&): "lvalue message"
[LOG] Processing argument...
processImpl(&&): "rvalue msg"
*/
Run command
g++ -std=c++11 -Wall -Wextra -o example perfect_forwarding.cpp