Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Structured bindings with tuple shows incorrect tuple_element usage #277

Closed
brevzin opened this issue Dec 2, 2019 · 1 comment
Closed
Labels
enhancement New feature or request

Comments

@brevzin
Copy link

brevzin commented Dec 2, 2019

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.

@andreasfertig
Copy link
Owner

Hello @brevzin,

thank you for spotting and reporting this. I never realized it and I'm still not sure what the reason is. I'm with you that it is odd.

Let's walk through it.

#include <tuple>

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

Look at it in C++ Insights. There is appears as you expect it, correct? The difference is, that in this link I switch the STL to libc++ (libstdc++ is the default). This seems to have an influence.

With a different custom structured binding implementation:

Here the STL does have no influence. However, in the AST (using godbolt) line 92 and 94 show:

    |   | `-DeclRefExpr <col:10> 'std::tuple_element<0, Point>::type':'int' lvalue Var 0x55c7e17549f0 'x' 'std::tuple_element<0, Point>::type &'
    |   `-BindingDecl <col:13> col:13 y 'std::tuple_element<0, Point>::type':'int'
    |     `-DeclRefExpr <col:13> 'std::tuple_element<0, Point>::type':'int' lvalue Var 0x55c7e1755718 'y' 'std::tuple_element<0, Point>::type &'

which is what C++ Insights prints.

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

I agree that it should be either the final type (int) or tuple_element. The first one I think is easier. With that the index issue would be solved as well.
I will see what I can do there, either ways it is a special case.

Andreas

@andreasfertig andreasfertig added the enhancement New feature or request label Dec 2, 2019
andreasfertig added a commit that referenced this issue Mar 1, 2021
Fixed #277: Consistently show the underlaying type of a binding.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants