Modern C++ code uses auto and {} everywhere; knowing their exact rules avoids silent copies and surprising vector sizes.
auto asks the compiler to deduce a variable's type from its initializer: auto n = 42; is an int, auto d = 2.5; a double. It shines with long type names (iterators, lambdas) and keeps code correct when types change.
The rule to remember: plain auto makes a copy and drops top-level const and references. If ref is a const std::vector<int>&, then auto v = ref; copies the whole vector. Write const auto& v = ref; to bind without copying, or auto& when you want to modify the original.
C++ has several initialization syntaxes. Brace initialization works for everything and is the safest:
int x{5};orint x = {5};int zero{};value-initializes to0(alsonullptr,false, empty objects).- Braces reject narrowing:
int n{3.7};is a compile error, whileint n = 3.7;silently truncates to3. Widget w{};always creates an object, whileWidget w();declares a function (the "most vexing parse").
The one big gotcha: if a class has a constructor taking std::initializer_list, braces prefer it. std::vector<int> a(3, 7); is three sevens, but std::vector<int> b{3, 7}; is the two elements 3 and 7. Use parentheses when you mean "count and value".
Another subtle one: auto s = "hi"; deduces const char*, not std::string. Write std::string s = "hi"; or use the s literal suffix.
Use auto where the type is obvious or noisy, and spell the type where it documents intent.
Example
#include <iostream>
#include <string>
#include <vector>
int main() {
auto n = 42; // int
auto d = 2.5; // double
auto s = std::string{"hi"};
int zero{}; // value-initialised: 0
// int bad{3.7}; // error: narrowing conversion
int truncated = 3.7; // compiles, silently 3 (warns with -Wconversion)
std::vector<int> a(3, 7); // three elements: 7 7 7
std::vector<int> b{3, 7}; // two elements: 3 7 (initializer_list wins)
const std::vector<int>& ref = a;
auto copy = ref; // std::vector<int>: const and & dropped, real copy
const auto& alias = ref; // no copy
std::cout << n + d << ' ' << s << ' ' << zero << ' ' << truncated << '\n'; // 44.5 hi 0 3
std::cout << a.size() << ' ' << b.size() << ' '
<< copy.size() << ' ' << alias.size() << '\n'; // 3 2 3 3
}Remember
autocopies and dropsconst/&; useconst auto&to avoid copies- Braces forbid narrowing and
{}zero-initialises - Braces prefer
initializer_listconstructors:vector{3, 7}has two elements
Watch out for
- Writing
std::vector<int> v{n};expecting n elements — it creates one element with value n.