Skip to content

Last updated:2026-10-12

C++17 Structured Bindings

Before talking about structured bindings, here are a few interview questions - can you answer them?

  • How do you access the private members of a custom class using structured bindings?

  • How do you modify the members of a custom class using structured bindings?

Few people can answer these questions, and neither the questions nor the answers can be found online. Based on these questions, this article introduces its topic: how to support structured bindings for custom types, and what pitfalls lie along the way.

Note: for the lazy, the answers and code are already available in the members' community.

To make this article flow more smoothly, let's start from an example.

1.Custom types

Given a class:

struct Foo {
  Foo(int64_t id, std::string data) : id_(id), data_(std::move(data)) {}
  void Print() { std::cout << "id = " << id_ << ", data = " << data_ << std::endl; }

 private:
  int64_t id_;
  std::string data_;
};

To make the following code run:

int main() {
  Foo f{12, "hello world"};
  const auto& [id, data] = f;
  std::cout << "id = " << id << ", data = " << data << std::endl;
  f.Print();
}

If you run it directly, it fails to compile:

 cannot decompose inaccessible member 'Foo::id_' of 'const Foo'
   42 |   const auto& [id, data] = f;

So what do we do?

2.Supporting structured bindings

We solve this problem in two steps:

Step 1: specialize std::tuple_size and std::tuple_element

namespace std {
  template <>
  struct tuple_size<Foo> : std::integral_constant<std::size_t, 2> {};

  template <>
  struct tuple_element<0, Foo> {
    using type = int64_t;
  };

  template <>
  struct tuple_element<1, Foo> {
    using type = std::string;
  };
}

Step 2: add a friend get function for the struct above, then implement it:

template <std::size_t Index>
decltype(auto) get(const Foo& f);

template <>
decltype(auto) get<0>(const Foo& f) {
  return f.id_;
}

template <>
decltype(auto) get<1>(const Foo& f) {
  return f.data_;
}

Now it works correctly, producing the output:

id = 12, data = hello world
id = 12, data = hello world

Finally, here are a few questions to leave you with:

  • Above we provided read-only access; what happens if the bound objects are modified when accessing via const auto&?

For example:

auto& [id, data] = f;
  • How do you support modifying access to class members?

That is, make this statement work:

auto& [id, data] = f;

That's all for this section; feel free to discuss in the comments.

Comments