vector{n} That Holds One Element Called n2026-09-09
This function tallies votes for a small election. Each candidate gets a slot in a counter vector, votes come in as candidate indices, and out-of-range indices are treated as spoiled ballots and silently dropped.
#include <vector>
#include <iostream>
// Return a vector of `num_candidates` zeros to accumulate votes into.
std::vector<int> make_tally(int num_candidates) {
return std::vector<int>{num_candidates};
}
void cast_vote(std::vector<int>& tally, int candidate_index) {
if (candidate_index >= 0 && candidate_index < (int)tally.size()) {
tally[candidate_index]++;
}
// else: spoiled ballot, silently ignore
}
int main() {
auto tally = make_tally(3); // Alice=0, Bob=1, Carol=2
cast_vote(tally, 0); // Alice
cast_vote(tally, 1); // Bob
cast_vote(tally, 2); // Carol
std::cout << "size: " << tally.size() << "\n";
for (size_t i = 0; i < tally.size(); ++i)
std::cout << "candidate " << i << ": " << tally[i] << "\n";
}
Expected: three candidates, one vote each. Actual:
size: 1
candidate 0: 4
The culprit is one pair of curly braces in make_tally. In C++11's "uniform initialization," when a type has an initializer_list constructor, brace initialization prefers it over any other constructor, no matter how much better the other constructor's match would be.
std::vector<int> has two relevant constructors:
vector(size_type count) — creates count default-initialized elements.vector(std::initializer_list<int>) — creates a vector containing the listed elements.std::vector<int>{3} looks like "vector of size 3," but the compiler sees the braces, spots the initializer-list constructor, and greedily takes it. The result is a vector of size 1 whose sole element has value 3.
Now walk through the votes. Alice votes for index 0, which exists — it holds 3, and gets incremented to 4. Bob (index 1) and Carol (index 2) are out of range, so the bounds check quietly drops them. The election reports Alice with four votes and the other two candidates as if they never existed. The safety check that was supposed to catch bad input is instead hiding the corruption.
The fix is to use parentheses whenever you mean "call the sized constructor":
std::vector<int> make_tally(int num_candidates) {
return std::vector<int>(num_candidates); // ← parens, not braces
}
Or be explicit about the value too: std::vector<int>(num_candidates, 0). Both call the count-plus-value constructor and produce num_candidates zeros as intended.
This trap is especially insidious because uniform initialization is exactly what "modern C++" style guides recommend. It works correctly for std::array, aggregates, and most user-defined types. But for containers that accept initializer_list, brace initialization silently changes meaning based on element count and type. std::vector<int>{5, 10} is a two-element vector [5, 10], not a five-element vector of tens. std::vector<std::string>{5} would fail to compile (int isn't convertible to string) — so the bug hides most reliably in vectors of numeric types.
Rule of thumb: for sizing a container, always reach for (). Reserve {} for supplying the literal contents.
initializer_list constructor, brace initialization prefers it over every other constructor — so std::vector<int>{n} silently makes a 1-element vector containing n, not n zeros.
