How to Convert std::vector or std::deque to std::initializer_list in C++
When working with modern C++ libraries like Boost.Process, you may encounter APIs that accept parameters of type std::initializer_list<T> (such as std::initializer_list<std::string_view>). However, if your data is generated at runtime inside a standard dynamic container like std::vector or std::deque, you might wonder how to convert that container into a std::initializer_list.
The Short Answer: You Can't (And Shouldn't)
In standard C++, it is not possible to dynamically convert a std::vector or std::deque into a std::initializer_list at runtime.
This is because a std::initializer_list<T> is designed specifically for compile-time or expression-level braced initialization syntax (e.g., { "arg1", "arg2" }). Under the hood, the C++ compiler automatically allocates a temporary array on the stack to back the initializer list. The constructors for std::initializer_list are private or implementation-defined, preventing developers from manually constructing one with dynamic runtime data sizes.
Why APIs Use std::initializer_list (And Why It's Sometimes an Anti-Pattern)
Library authors often use std::initializer_list to allow clean, hardcoded syntax like this:
my_function({"option1", "option2", "option3"});However, taking std::initializer_list as a sole parameter type when dynamic runtime arguments are expected is widely considered an API code smell. Flexible C++ APIs should instead accept iterator pairs, std::span (C++20), or generic C++ ranges.
Solutions & Workarounds
Depending on whether you can modify the calling code or the library API, here are the standard ways to resolve this issue:
1. Look for Alternative Overloads or Range Support
Most well-designed libraries—including Boost—provide alternative overloads or helper functions for dynamic collections. For example, in Boost.Process, you do not have to rely strictly on the initializer list overload for arguments.
Instead of passing arguments directly into the constructor as an initializer list, check if the API accepts iterators, range types, or dedicated argument wrappers like boost::process::args:
#include <boost/process.hpp>
#include <vector>
#include <string>
namespace bp = boost::process;
int main() {
std::vector<std::string> dynamic_args = {"--verbose", "--output", "file.txt"};
// Pass dynamic vector using boost::process::args helper
bp::child c(bp::search_path("my_exec"), bp::args(dynamic_args));
c.wait();
}2. Use C++20 std::span for Your Own APIs
If you are designing an API or refactoring code that needs to accept both static braced lists and dynamic containers, use std::span (C++20) or a template range instead of std::initializer_list:
#include <span>
#include <string_view>
#include <iostream>
// std::span accepts both std::vector, std::array, and C-style arrays automatically
void execute_command(std::span<const std::string_view> args) {
for (auto arg : args) {
std::cout << arg << "\n";
}
}
int main() {
// Works with dynamic vector
std::vector<std::string_view> vec = {"arg1", "arg2"};
execute_command(vec);
// Also works with inline syntax
std::string_view arr[] = {"arg1", "arg2"};
execute_command(arr);
}Summary
std::initializer_listcannot be constructed dynamically from a runtime container likestd::vectororstd::deque.- Check the library documentation (e.g., Boost.Process) for alternative overloads taking iterators, vectors, or dedicated argument helpers.
- When writing modern C++ code, prefer
std::spanor template concepts overstd::initializer_listfor function parameters that need to accept standard containers.