C++14coreintermediate
decltype(auto)
C++14 · Modern Refined · Compiler flag: -std=c++14
Explanation
Deduces the type of a variable or return value using `decltype` semantics rather than `auto` semantics. While `auto` always strips references and top-level qualifiers, `decltype(auto)` preserves them, making it essential for perfect forwarding of return values in generic code.
Real-World Scenario
A caching proxy wraps access to a configuration store. Using `decltype(auto)`, the proxy returns a reference to the underlying value so callers can modify it in-place rather than receiving a copy.
Quick Snippet
snippet.cpp
#include <iostream>
#include <vector>
std::vector<int> data = {10, 20, 30};
decltype(auto) getElement(size_t index) {
return data[index]; // returns int&, not int
}
int main() {
getElement(1) = 99; // modifies data[1] in place
std::cout << data[1] << "\n"; // 99
}Full Example
decltype_auto.cpp
// CodeNebula - C++14 Example: decltype(auto)
// Compile: g++ -std=c++14 -Wall -Wextra -o example decltype_auto.cpp
// Demonstrates decltype(auto) which preserves the exact type including
// references and cv-qualifiers. Unlike plain 'auto' which strips references,
// decltype(auto) deduces the type exactly as decltype would. Real-world use:
// perfect forwarding of return types in wrapper/proxy functions.
#include <iostream>
#include <string>
#include <vector>
// ------------------------------------------------------------------
// Global data for reference examples
// ------------------------------------------------------------------
std::vector<int> global_data = {10, 20, 30, 40, 50};
std::string global_name = "CodeNebula";
// ------------------------------------------------------------------
// 1. auto strips references; decltype(auto) preserves them
// ------------------------------------------------------------------
auto get_copy(std::vector<int>& v)
{
return v[0]; // returns int (copy)
}
decltype(auto) get_ref(std::vector<int>& v)
{
return v[0]; // returns int& (reference!)
}
// ------------------------------------------------------------------
// 2. decltype(auto) with const references
// ------------------------------------------------------------------
decltype(auto) get_const_ref(const std::string& s)
{
return s; // returns const string&
}
// ------------------------------------------------------------------
// 3. Perfect return type forwarding in a wrapper
// ------------------------------------------------------------------
template<typename Container>
decltype(auto) front(Container& c)
{
return c.front(); // preserves whatever front() returns
}
// ------------------------------------------------------------------
// 4. decltype(auto) variables (less common but valid)
// ------------------------------------------------------------------
void demonstrate_variables()
{
int x = 42;
int& rx = x;
auto a = rx; // int (reference stripped)
decltype(auto) b = rx; // int& (reference preserved)
b = 100; // modifies x through the reference
std::cout << " auto a = rx -> a = " << a
<< " (copy, x unchanged by a)" << std::endl;
std::cout << " decltype(auto) b -> b = " << b
<< " (reference, x changed to 100)" << std::endl;
std::cout << " x after b = 100 -> x = " << x << std::endl;
}
int main()
{
// ------------------------------------------------------------------
// 1. Reference preservation
// ------------------------------------------------------------------
std::cout << "=== auto vs decltype(auto) Return ===" << std::endl;
std::cout << " Original v[0] : " << global_data[0] << std::endl;
auto copy = get_copy(global_data);
copy = 999;
std::cout << " After copy = 999 : v[0] = " << global_data[0]
<< " (unchanged, was a copy)" << std::endl;
decltype(auto) ref = get_ref(global_data);
ref = 999;
std::cout << " After ref = 999 : v[0] = " << global_data[0]
<< " (changed, was a reference)" << std::endl;
std::cout << std::endl;
// ------------------------------------------------------------------
// 2. Const reference preservation
// ------------------------------------------------------------------
std::cout << "=== Const Reference Preservation ===" << std::endl;
decltype(auto) name_ref = get_const_ref(global_name);
std::cout << " name_ref: " << name_ref << std::endl;
// name_ref = "other"; // Would fail: const reference
std::cout << " (const ref - cannot modify)" << std::endl;
std::cout << std::endl;
// ------------------------------------------------------------------
// 3. Perfect forwarding with containers
// ------------------------------------------------------------------
std::cout << "=== Perfect Return Forwarding ===" << std::endl;
std::vector<double> doubles = {1.1, 2.2, 3.3};
front(doubles) = 9.9; // works because front() returns a reference
std::cout << " After front(doubles) = 9.9 : " << doubles[0] << std::endl;
std::cout << std::endl;
// ------------------------------------------------------------------
// 4. Variable declarations
// ------------------------------------------------------------------
std::cout << "=== decltype(auto) Variables ===" << std::endl;
demonstrate_variables();
return 0;
}
/*
Expected output:
=== auto vs decltype(auto) Return ===
Original v[0] : 10
After copy = 999 : v[0] = 10 (unchanged, was a copy)
After ref = 999 : v[0] = 999 (changed, was a reference)
=== Const Reference Preservation ===
name_ref: CodeNebula
(const ref - cannot modify)
=== Perfect Return Forwarding ===
After front(doubles) = 9.9 : 9.9
=== decltype(auto) Variables ===
auto a = rx -> a = 42 (copy, x unchanged by a)
decltype(auto) b -> b = 100 (reference, x changed to 100)
x after b = 100 -> x = 100
*/
Run command
g++ -std=c++14 -Wall -Wextra -o example decltype_auto.cpp