Skip to content

Structured bindings with tuple shows incorrect tuple_element usage #277

@brevzin

Description

@brevzin

This example:

#include <tuple>

int f(std::tuple<int, double> x) {
  auto [i, j] = x;
  return i;
}

expands as:

#include <tuple>

int f(std::tuple<int, double> x)
{
  std::tuple<int, double> __x4 = std::tuple<int, double>(x);
  std::tuple_element<0, std::tuple<int, double> >::type& i = std::get<0UL>(__x4);
  std::tuple_element<0, std::tuple<double> >::type& j = std::get<1UL>(__x4);
  return i;
}

This is equivalent, but I think you really want std::tuple_element<1, std::tuple<int, double> >::type there instead (since that's what the specification says).

With a different custom structured binding implementation:

#include <tuple>

struct Point {
  	int data[2];
};

template <std::size_t N>
constexpr int& get(Point& p) {
  return p.data[N];
}

namespace std {
  template <> struct tuple_size<Point> : integral_constant<size_t, 2> { };
  template <size_t N> struct tuple_element<N, Point> : tuple_element<N-1, Point> { };
  template <> struct tuple_element<0, Point> { using type = int; };
}

int f(Point p) {
  auto& [x, y] = p;
  return x;
}

the binding there shows up as:

int f(Point p)
{
  Point & __p19 = p;
  std::tuple_element<0, Point>::type& x = get<0UL>(__p19);
  std::tuple_element<0, Point>::type& y = get<1UL>(__p19);
  return x;
}

Instead of std::tuple_element<1, Point>::type.

Which gets even more confusing if I wrote it this way:

#include <tuple>

struct Point {
  	int data[2];
};

template <std::size_t N>
constexpr int& get(Point& p) {
  return p.data[N];
}

template <typename T> struct type_t { using type = T; };

namespace std {
  template <> struct tuple_size<Point> : integral_constant<size_t, 2> { };
  template <size_t N> struct tuple_element<N, Point> : type_t<int> { };
}

int f(Point p) {
  auto& [x, y] = p;
  return x;
}

because then I get:

int f(Point p)
{
  Point & __p20 = p;
  type_t<int>::type& x = get<0UL>(__p20);
  type_t<int>::type& y = get<1UL>(__p20);
  return x;
}

I think in the customization point case, prefer to keep tuple_element<N, T>::type and not go through base classes of the particular tuple_element instantiation? Or, if you do, then go all the way and rewrite the above as int& instead of going most of the way through the instantiation process.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions