As a C++ developer, you may have encountered the following compiler error when trying to declare a variable length array (VLA):
error: ISO C++ forbids variable length array
This error occurs because VLAs have their size set at runtime, but C++ requires array dimensions to be known at compile time. While prohibiting VLAs provides some benefits for program correctness and optimization, VLAs can also be useful in many contexts. This article will demonstrate several effective techniques for emulating variable length array behavior in standard C++ code.
The Allure and Utility of Variable Length Arrays
First, why might a developer want VLA functionality in C++ despite the errors?
Flexibility – VLAs allow an array‘s size to be set dynamically based on program logic or user input. This flexibility is needed in cases like:
- Reading/processing input data files or streams where the size is not known upfront
- Allocating space based on complex runtime calculations
- Memory pooling optimized to current resource usage
Performance – Stack allocation can be faster than heap allocation in some usage contexts. VLAs tap into stack space for temporary storage.
Memory Control – Manually pushing arrays onto the stack can optimize memory control compared to full automation.
Porting C Code – VLAs are valid and widely used in C code, which creates migration challenges.
These reasons and the ubiquity of VLAs in C make their prohibition in ISO C++ a vexing limitation for some. While the language designers had sound rationale, there are still methods to fulfill VLA intent idiomatically in standard C++ if you understand the available tools.
Emulating VLAs in Standard C++
Here we will outline several flexible array programming techniques:
1. std::vector Offers Dynamic Resizing Ability
The easiest and most widely applicable approach for obtaining variable length array behavior is to use std::vector. The key capability vector provides is dynamic resizing as more elements get pushed in:
#include <iostream>
#include <vector>
int main() {
// Start empty
std::vector<int> vec;
// Add elements
vec.push_back(10);
vec.push_back(20);
// Check size
std::cout << "Num Elements: " << vec.size() << "\n"; // 2
// Resize
vec.resize(5);
std::cout << "Num Elements: " << vec.size() << "\n"; // 5
}
In early C++ standards, vector was less efficient than manually managed arrays – causing unnecessary allocations/copying on growth. But modern C++ implementations have optimized these operations through buffer preallocation/inlining/moving, making vector suitable even for performance-critical domains like gaming and graphics.
For most applications, std::vector delivers the benefits of VLAs without drawbacks:
- Performance now nearly equivalent to raw arrays
- Intuitive API
- Bounds checking in debug builds
- Efficient growth from preallocated buffers
Utilizing vector also automatically future-proofs code to leverage continuing performance innovations from newer compiler versions.
However, one distinction from pure VLAs is vectors reside on the heap rather than stack. We explore alternatives for stack allocation next.
2. Pointers Offer Manual Control Over Stack Allocation
While less safe/convenient than vector, C++ pointers afford direct access to stack memory for flexible arrays:
void my_func() {
int arr_len = compute_len(); // dynamic size
int* arr = new int[arr_len]; // allocated on stack
// ... use array ...
delete [] arr;
}
The key aspects:
- Pointer
arrmanually allocatesarr_lenints on the stack - Freed manually later with
delete[]to prevent leaks
Compared to vectors, tradeoffs include:
- No resize ability – size is fixed when allocated
- Risk of leaks/dangling references without proper deletion
- Lose convenience of vector‘s methods
Pointers do allow full manual control, which can have performance advantages in scenarios where sizes and access patterns are predictable. This works best when the lifetime of the array slots into clear scopes so deallocation is obvious.
3. std::dynarray (C++23) Bridges Stack VLA Needs
For directly usable stack allocation, the upcoming C++23 standard includes a new std::dynarray specifically designed to offer VLA convenience through a stack-allocated container:
// In C++23
std::dynarray<int> da(n); // Stack allocated
Advantages of std::dynarray include:
- Stack allocation where size allows
- Auto fall-back to heap allocation for larger dynarrays (no explicit new/delete)
- Container interface with convenience methods like size()/iterators
The dynarray class will cover most common VLA use cases with better safety and consistency than manual pointers.
4. Compiler Extensions Allow VLAs As a Non-Standard Extension
Some compilers like GCC and Clang support VLAs directly in C++ as language extensions (with flags like -std=gnu++2a).
For example, this compiles on GCC 9+:
// Compiler extension
int n = get_size();
int arr[n]; // VLA
This permits portable C code relying on VLAs to build under C++ with minimal modification. However we recommend avoiding non-standard language extensions for production code, as they inhibit general compatibility.
Think of compiler VLA extensions primarily as a migration/porting aid when bringing C code to C++builds. Lean on standard solutions long-term for robustness.
5. Encapsulate C VLAs Calls
A final approach allowing VLAs in C++ code is encapsulating alloc/access calls in external C functions/libraries. Since C permits VLAs natively, the functionality can be tapped by calling out:
// alloc.c
void populate_array(int n, int arr[n]) {
for(int i = 0; i < n; i++) {
arr[i] = i;
}
}
// main.cpp
extern "C" {
#include "alloc.c"
}
int main() {
int arr[10];
populate_array(10, arr);
}
This strategy maintains standards compliance in C++, using C functions just for VLA manipulation. But bridging between languages introduces complexities worth avoiding if possible.
Key Recommendations
Based on our experience, here are best practice recommendations:
-
Prefer
std::vectorfor most dynamic array needs – Maximizes flexibility alongside performance and safety. Modern compilers have largely eliminated the performance gaps with manual allocation. -
Minimize raw pointers for direct allocation – More prone to memory/lifecycle issues. Consider a typed wrapper class if going this route to enforce good practices.
-
Plan a migration to
std::dynarraypost-C++23 for direct stack allocation cases not addressable by vector. -
Use compiler extensions minimally as a porting aid – Avoid long-term reliance for production code compatibility.
Following these guidelines will ensure portable array allocation without sacrificing flexibility or performance. We hope this provides clarity for overcoming the VLA prohibition – helping resolve those vexing ISO C++ errors! Please reach out with any other dynamic array questions.


