Capturing Lvalues and Rvalues in Variadic Functions

How can I write a variadic function that can accept both lvalue and rvalue arguments?

To write a variadic function that can accept both lvalue and rvalue arguments, you can use forwarding references (also known as universal references) in the parameter pack. Forwarding references are declared using the && syntax with a deduced type (auto or template type parameter).

Here's an example:

#include <iostream>

// Variadic function using forwarding references
template <typename... Types>
void PrintValues(Types&&... Args) {
  (std::cout << ... << std::forward<Types>(Args))
    << '\n';  
}

int main() {
  int a = 10;
  const std::string b = "hello";
  
  // lvalues
  PrintValues(a, b);

  // rvalues
  PrintValues(20, std::string("world"));
}
10hello
20world

In this example, PrintValues() uses forwarding references Types&&... Args to capture both lvalue and rvalue arguments. Inside the function, std::forward<Types>(Args) is used to forward the arguments to std::cout while preserving their value category.

When PrintValues() is called with lvalue arguments a and b, the compiler deduces the template arguments as int& and const std::string& respectively. The arguments are forwarded as lvalue references.

When PrintValues() is called with rvalue arguments 20 and std::string("world"), the compiler deduces the template arguments as int&& and std::string&& respectively. The arguments are forwarded as rvalue references.

By using forwarding references, the variadic function can accept and forward both lvalue and rvalue arguments, avoiding unnecessary copying and enabling perfect forwarding.

It's important to note that if you don't need to modify the arguments and want to avoid potential dangling references, you can use const Types&... instead of Types&&... to capture lvalues and rvalues as const references.

template <typename... Types>
void PrintValuesConstRef(const Types&... Args) {
  (std::cout << ... << Args) << '\n';
}

This version captures both lvalues and rvalues as const references, preventing modification and avoiding dangling references, but it does not enable perfect forwarding.

Variadic Functions

An introduction to variadic functions, which allow us to pass a variable quantity of arguments

Questions & Answers

Answers are generated by AI models and may not have been reviewed. Be mindful when running any code on your device.

Passing Parameter Packs by Value or Reference
When should I pass the parameters in a parameter pack by value, reference, const reference, or forwarding reference?
Recursion vs Fold Expressions
When should I use fold expressions over recursive variadic function calls?
Zero Argument Variadic Functions
What happens if I call a variadic function with no arguments?
Performance Considerations with Variadic Functions
Are there any performance implications to using variadic functions?
Why use parameter packs instead of just va_args?
I can already write variadic functions in C++ using va_args. Why would I use parameter packs instead?
Or Ask your Own Question
Get an immediate answer to your specific question using our AI assistant