Does structured binding work with std::vector?

Structured binding only works if the structure is known at compile time. This is not the case for the vector.

While you do know the structure of the individual elements, you do not know the number of elements, and that is what you are trying to decompose on in your question. Similarly, you can only use structured bindings on array types where the size is known at compile time. Consider:

void f(std::array<int, 3> arr1,
       int (&arr2)[3],
       int (&arr3)[])
{
    auto [a1,b1,c1] = arr1;
    auto [a2,b2,c2] = arr2;
    auto [a3,b3,c3] = arr3;
}

The first two will work, but the last line will fail to compile, because the size of arr3 is not known at compile time. Try it on godbolt.


It's easy enough to create a basic wrapper over your vector that gives access to it like a tuple. Since there is indeed no way to retrieve a vector's size at compile time, this throws std::out_of_range if you attempt to destructure too short a vector. Unfortunately I don't know of a way to deduce the number of requested bindings, so that's explicit.

Full code:

#include <string>
#include <vector>
#include <iostream>

template <class T, std::size_t N>
struct vector_binder {
    std::vector<T> &vec;

    template <std::size_t I>
    T &get() {
        return vec.at(I);
    }
};

namespace std {
    template<class T, std::size_t N>
    struct tuple_size<vector_binder<T, N>>
    : std::integral_constant<std::size_t, N> { };

    template<std::size_t I, std::size_t N, class T>
    struct tuple_element<I, vector_binder<T, N>> { using type = T; };
}

template <std::size_t N, class T>
auto dissect(std::vector<T> &vec) {
    return vector_binder<T, N>{vec};
}

int main() {
    std::vector<int> v{1, 2, 3};
    auto [a, b] = dissect<2>(v);

    a = 5;
    std::cout << v[0] << '\n'; // Has changed v through a as expected.
}

Rvalue and const versions of vector_binder as well as better names are left as an exercise to the reader :)

See it live on Coliru


Not ideal since it's more verbose but you can also do:

auto [a, b, c] = array<int, 3>({vec[0], vec[1], vec[2]});

I do not agree with the notion that not knowing the number of elements of a container should prevent structured binding to it's elements. My reasoning is that since the following does not throw a compile time error:

auto a = vec[0];
auto b = vec[1];
auto c = vec[2];

(even though vec[2] might be out of range at run-time), so should be the case for the above structured binding. Meaning, why not leave it to the user to make sure vector has the correct length at runtime, and throw an out of range exception if that is not the case? That is essentially how we use vectors everywhere else in the language.