Skip to content

structured binding - wrong value category passed to the get function. #381

@dawidpilarski

Description

@dawidpilarski

Having a simple code like:

#include <tuple>

int main()
{
    std::tuple<int, int> tup{2,5};
    auto [a, b] = tup;
}

results in following output from cppinsights:

#include <tuple>

int main()
{
  std::tuple<int, int> tup = std::tuple<int, int>{2, 5};
  std::tuple<int, int> __tup6 = std::tuple<int, int>(tup);
  std::tuple_element<0, std::tuple<int, int> >::type & a = std::get<0UL>(__tup6);
  std::tuple_element<0, std::tuple<int> >::type & b = std::get<1UL>(__tup6);
}

Even though it looks fine, it's not accurate, because, when __tup6 is not of lvalue reference type, which is the case here, then
xvalue is passed to the get function. You can have a look at a wording here:

https://eel.is/c++draft/dcl.struct.bind#4.sentence-7

In either case, e is an lvalue if the type of the entity e is an lvalue reference and an xvalue otherwise.

here e is our __tup6.

Also std::get has its implementations for lvalue references, rvalue references and const lvalue references, which is why it works correctly.

So the code produced should be in fact:

#include <tuple>

int main()
{
  std::tuple<int, int> tup = std::tuple<int, int>{2, 5};
  std::tuple<int, int> __tup6 = std::tuple<int, int>(tup);
  std::tuple_element<0, std::tuple<int, int> >::type & a = std::get<0UL>(std::move(__tup6));
  std::tuple_element<0, std::tuple<int> >::type & b = std::get<1UL>(std::move(__tup6));
}

when structured binding is done as auto [...] and stay as it is right now when it's of the form auto& [...].

Also you can have a look at at the code that will not compile, because of the above:

#include <tuple>
struct myStruct{
    int a = 1;
    int b = 2;
};

template<std::size_t idx>
auto& get(myStruct& str){
    if constexpr (idx == 0) return str.a;
    else return str.b;
}

namespace std{
template <>
struct tuple_size<myStruct>{
    static constexpr std::size_t value = 2;
};

template<>
struct tuple_element<0, myStruct>{
    using type = int&;
};

template<>
struct tuple_element<1, myStruct>{
    using type = int&;
};
}

int main()
{
    myStruct var;
    auto [a, b] = var;
}

it fails with error:

candidate function template not viable: expects an l-value for 1st argument.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions