Sebelum C++11, C++ punya empat cara inisialisasi dengan aturan berbeda-beda: int a = 5;, int arr[] = {1,2,3};, Poin p(3,4);, dan member initializer list : x(3). Masing-masing tidak saling tertukar — vector bisa diisi daftar elemen, struct biasa tidak; ada tipe yang butuh (, ada yang butuh =, dan ada kasus sintaks Jam j(); yang bukan inisialisasi sama sekali. C++11 memperkenalkan uniform initialization dengan kurung kurawal {}: satu sintaks untuk semua kategori tipe, dengan dua hadiah tambahan — penolakan narrowing conversion dan berakhirnya most vexing parse. Chapter ini membedah keduanya beserta satu jebakan yang paling sering mengejutkan pemula: constructor std::initializer_list yang selalu menang.
Satu Sintaks untuk Semua Tipe #
// uniform-init.cpp — satu sintaks {} untuk semuanya, plus jebakan yang wajib dikenali
// Kompilasi: g++ -std=c++23 -Wall -Wextra uniform-init.cpp -o uniform-init
#include <initializer_list>
#include <iostream>
#include <string>
#include <vector>
struct Poin { // aggregate: struct biasa tanpa constructor
int x;
int y;
};
struct Jam {
int detik = 0;
explicit Jam(int d) : detik(d) {}
Jam() = default;
};
struct Paket { // punya DUA constructor: dipilih sesuai bentuk {}
Paket(int a, int b) {
std::cout << "Paket: ctor 2 argumen (" << a << "," << b << ")\n";
}
Paket(std::initializer_list<int> li) {
std::cout << "Paket: ctor initializer_list (" << li.size() << " elemen)\n";
}
};
void tampil(const std::string& label, const std::vector<int>& v) {
std::cout << label << " -> " << v.size() << " elemen:";
for (int x : v) std::cout << " " << x;
std::cout << "\n";
}
int main() {
// satu sintaks untuk semua kategori tipe
int a{5};
double pi{3.14};
std::string kata{"modern"};
Poin p{3, 4}; // aggregate init
std::vector<std::string> nama{"budi", "dewi", "siti"};
Jam j{45}; // explicit constructor tetap dipakai
std::cout << "a=" << a << " pi=" << pi << " kata=" << kata << "\n";
std::cout << "p=(" << p.x << "," << p.y << ") "
<< "nama[0]=" << nama[0] << " jam=" << j.detik << "\n";
Jam defaultJam{}; // value-initialization -> member default
std::cout << "jam kosong=" << defaultJam.detik << "\n";
// initializer_list menang atas constructor lain saat ditulis {}
std::vector<int> v1{3}; // initializer_list: 1 elemen bernilai 3
std::vector<int> v2(3); // constructor count : 3 elemen bernilai 0
std::vector<int> v3(3, 7); // constructor count+nilai
std::vector<int> v4{3, 7}; // initializer_list: 2 elemen {3, 7}
tampil("v1{3} ", v1);
tampil("v2(3) ", v2);
tampil("v3(3,7)", v3);
tampil("v4{3,7}", v4);
std::cout << "-- pemilihan constructor pada Paket --\n";
Paket p1{1, 2}; // initializer_list lebih "cocok", ia yang dipilih
Paket p2(1, 2); // kurung memaksa ctor 2 argumen
return 0;
}Output yang Dihasilkan:
a=5 pi=3.14 kata=modern
p=(3,4) nama[0]=budi jam=45
jam kosong=0
v1{3} -> 1 elemen: 3
v2(3) -> 3 elemen: 0 0 0
v3(3,7) -> 3 elemen: 7 7 7
v4{3,7} -> 2 elemen: 3 7
-- pemilihan constructor pada Paket --
Paket: ctor initializer_list (2 elemen)
Paket: ctor 2 argumen (1,2)Mari kita bedah:
- Baris 27-32: skalar, string, aggregate, container, dan class berconstructor semuanya diisi dengan
{}. Inilah “uniform”: satu bentuk mental untuk semua kasus. - Baris 28 (
Poin p{3, 4}): inisialisasi aggregate — compiler memasangkan3kexdan4keysecara berurutan tanpa constructor. C++20 menambah varian dengan nama member:Poin p{.x = 3, .y = 4}. - Baris 35 (
Jam defaultJam{}):{}tanpa isi memicu value-initialization — member yang punya default (int detik = 0) diisi nilai bawaannya. Kontras denganint x;tanpa inisialisasi yang berisi nilai acak. - Baris 38-41 (jebakan utama):
v1{3}membuat vector berisi satu elemen bernilai 3, bukan tiga elemen kosong. Yang membuat tiga elemen adalahv2(3)dengan tanda kurung. Ini perbedaan yang paling sering menimbulkan bug halus. - Baris 52-54 (Paket): bila class punya constructor
initializer_list, bentuk{...}selalu memilihnya — meski ada constructor yang lebih pas argumennya. Hanya tanda kurung(...)yang bisa memaksakan constructor biasa.
{} Menolak Narrowing #
Narrowing conversion adalah penyusutan nilai yang bisa kehilangan data — double ke int, long long ke int, int ke char. Sintaks lama menerimanya diam-diam; {} menolaknya saat kompilasi:
int salah{3.14};
// error: narrowing conversion of '3.1400000000000001e+0'
// from 'double' to 'int' [-Wnarrowing]
char huruf{300};
// error: narrowing conversion of '300' from 'int' to 'char' [-Wnarrowing]
int ok = 3.14; // sintaks lama: lolos, salah berubah diam-diam menjadi 3
Pesan error pertama persis seperti yang dihasilkan g++ 14.2. Konversi yang aman (misalnya int x{5}; dari literal 5 yang diketahui pas) tetap diperbolehkan, sehingga aturan ini mencegah kehilangan data tanpa mengganggu kasus normal.
Most Vexing Parse Terselesaikan #
Ada kasus legendaris di C++: baris yang kita maksud “buat objek” tetapi compiler membacanya “deklarasikan fungsi”. Dengan sintaks kurung, bahkan compiler pun harus menebak — dan g++ kini memberi peringatan khusus bernama -Wvexing-parse:
warning: empty parentheses were disambiguated as a function declaration [-Wvexing-parse]
4 | Jam j(); // PENGECAT: deklarasi fungsi bernama j!
| ^~
note: remove parentheses to default-initialize a variable
note: or replace parentheses with braces to aggregate-initialize a variableJam j(); adalah deklarasi fungsi j yang mengembalikan Jam — bukan objek. Dengan {}, keambiguan itu hilang karena kurung kurawal tidak punya makna deklarasi fungsi:
Jam j(); // deklarasi fungsi (dengan peringatan compiler)
Jam j{}; // objek Jam — pasti, tanpa keambiguan
Aturan praktisnya: pakai {} sebagai bawaan (default) untuk inisialisasi, karena ia menyatukan semua tipe, menolak penyusutan nilai, dan bebas dari parse berbahaya; pakai (...) hanya saat maksud Anda persis memilih constructor non-initializer_list, seperti std::vector<int> v(1000).
Dengan fondasi inisialisasi yang sehat, langkah berikutnya masuk ke sisi lain dari sama koinnya: bagaimana compiler menyimpulkan tipe dari inisialisasi itu — topik penuh auto, decltype, dan aturan deduksinya di chapter berikutnya.
Rujukan resmi: cppreference — list initialization , cppreference — initializer_list .