⚡
Advanced C++ Features
Templates, STL, modern C++ features, and performance optimization
Templates and Generic Programming
Function and Class Templates
#include <iostream>
#include <vector>
#include <string>
// Function template
template<typename T>
T getMax(T a, T b) {
return (a > b) ? a : b;
}
// Template with multiple parameters
template<typename T, typename U>
auto add(T a, U b) -> decltype(a + b) { // C++11 trailing return type
return a + b;
}
// Class template
template<typename T>
class Stack {
private:
std::vector<T> elements;
public:
void push(const T& element) {
elements.push_back(element);
}
void pop() {
if (!elements.empty()) {
elements.pop_back();
}
}
T& top() {
return elements.back();
}
bool empty() const {
return elements.empty();
}
size_t size() const {
return elements.size();
}
};
// Template specialization
template<>
class Stack<bool> {
private:
std::vector<char> elements; // Use char instead of bool for efficiency
public:
void push(bool element) {
elements.push_back(element ? 1 : 0);
}
void pop() {
if (!elements.empty()) {
elements.pop_back();
}
}
bool top() {
return elements.back() != 0;
}
bool empty() const {
return elements.empty();
}
size_t size() const {
return elements.size();
}
};
int main() {
// Function template usage
std::cout << "Max of 10, 20: " << getMax(10, 20) << std::endl;
std::cout << "Max of 3.14, 2.71: " << getMax(3.14, 2.71) << std::endl;
std::cout << "Max of 'a', 'z': " << getMax('a', 'z') << std::endl;
// Mixed type template
std::cout << "Add 5 + 3.14: " << add(5, 3.14) << std::endl;
// Class template usage
Stack<int> intStack;
intStack.push(10);
intStack.push(20);
intStack.push(30);
std::cout << "Stack size: " << intStack.size() << std::endl;
std::cout << "Top element: " << intStack.top() << std::endl;
Stack<std::string> stringStack;
stringStack.push("Hello");
stringStack.push("World");
while (!stringStack.empty()) {
std::cout << stringStack.top() << " ";
stringStack.pop();
}
std::cout << std::endl;
return 0;
}Standard Template Library (STL)
Containers, Iterators, and Algorithms
#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <algorithm>
#include <iterator>
#include <numeric>
int main() {
// Containers
std::vector<int> vec = {5, 2, 8, 1, 9, 3};
std::list<std::string> lst = {"apple", "banana", "cherry"};
std::map<std::string, int> ages = {{"Alice", 25}, {"Bob", 30}, {"Charlie", 35}};
std::set<int> uniqueNumbers = {1, 2, 3, 2, 1, 4, 5};
// Iterators
std::cout << "Vector elements: ";
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
// Range-based for loop (C++11)
std::cout << "List elements: ";
for (const auto& item : lst) {
std::cout << item << " ";
}
std::cout << std::endl;
// Algorithms
std::sort(vec.begin(), vec.end());
std::cout << "Sorted vector: ";
for (int n : vec) {
std::cout << n << " ";
}
std::cout << std::endl;
// Find algorithm
auto found = std::find(vec.begin(), vec.end(), 5);
if (found != vec.end()) {
std::cout << "Found 5 at position: " << std::distance(vec.begin(), found) << std::endl;
}
// Transform algorithm
std::vector<int> squared(vec.size());
std::transform(vec.begin(), vec.end(), squared.begin(), [](int n) { return n * n; });
std::cout << "Squared elements: ";
for (int n : squared) {
std::cout << n << " ";
}
std::cout << std::endl;
// Accumulate
int sum = std::accumulate(vec.begin(), vec.end(), 0);
std::cout << "Sum: " << sum << std::endl;
return 0;
}Modern C++ Features (C++11/14/17/20)
Lambda Functions, Auto, and More
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <memory>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// Lambda functions (C++11)
auto isEven = [](int n) { return n % 2 == 0; };
auto square = [](int n) { return n * n; };
// Count even numbers
int evenCount = std::count_if(numbers.begin(), numbers.end(), isEven);
std::cout << "Even numbers: " << evenCount << std::endl;
// Lambda with capture
int multiplier = 3;
auto multiplyBy = [multiplier](int n) { return n * multiplier; };
// Transform with lambda
std::vector<int> multiplied(numbers.size());
std::transform(numbers.begin(), numbers.end(), multiplied.begin(), multiplyBy);
// Auto keyword (C++11)
auto result = 42; // int
auto pi = 3.14159; // double
auto name = std::string("C++");
// Range-based for loop with auto
for (const auto& num : multiplied) {
std::cout << num << " ";
}
std::cout << std::endl;
// Smart pointers (C++11)
auto ptr = std::make_unique<int>(42);
auto sharedPtr = std::make_shared<std::string>("Hello");
std::cout << "Unique ptr: " << *ptr << std::endl;
std::cout << "Shared ptr: " << *sharedPtr << std::endl;
// Move semantics (C++11)
std::vector<int> vec1 = {1, 2, 3, 4, 5};
std::vector<int> vec2 = std::move(vec1); // Move, don't copy
std::cout << "vec1 size after move: " << vec1.size() << std::endl;
std::cout << "vec2 size: " << vec2.size() << std::endl;
return 0;
}Performance Optimization
✅ Best Practices
- • Use const references for large objects
- • Prefer smart pointers over raw pointers
- • Use move semantics for expensive operations
- • Reserve vector capacity when size is known
- • Use algorithms instead of manual loops
- • Enable compiler optimizations (-O2, -O3)
❌ Avoid
- • Unnecessary copying of large objects
- • Memory leaks with raw pointers
- • Premature optimization
- • Using C-style arrays instead of std::array
- • Ignoring const correctness
- • Deep inheritance hierarchies