英文:
Auto does not work with stuctured binding
问题
I'm trying to understand how structured bindings for custom types work in practice. The following code does not compile with an error:
错误:无法将类型为‘Customer&’的非const左值引用绑定到类型为‘Customer’的右值。
I'm probably missing some fundamental understanding. If I either replace auto with auto& or Customer& with const Customer& everything compiles fine. Why rvalue of Customer is mentioned in the error message?
我可能缺乏一些基本理解。如果我将 auto 替换为 auto& 或将 Customer& 替换为 const Customer&,则一切都会编译成功。为什么错误消息中提到了 Customer 的右值?
英文:
I'm trying to understand how structured bindings for custom types work in practice. The following code does not compile with an error:
error: cannot bind non-const lvalue reference of type ‘Customer&’ to an rvalue of type ‘Customer
I'm probably missing some fundamental understanding. If I either replace auto with auto& or Customer& to with const Customer& everything compiles fine. Why rvalue of Customer is mentioned in the error message?
#include <iostream>
#include <tuple>
struct Customer
{
int id = 10;
};
template<>
struct std::tuple_size<Customer> : std::integral_constant<std::size_t, 1> {};
template<>
struct std::tuple_element<0, Customer>
{
using type = int;
};
template<std::size_t id>
auto get(Customer& customer)
{
if constexpr (id == 0)
{
return customer.id;
}
}
int main() {
Customer customer;
auto [id] = customer; // Ouch, error
std::cout << id;
}
答案1
得分: 6
get 函数传递的对象在您的情况下被定义为 xvalue,而不是直接是 customer。
从 cppreference:
结构化绑定声明首先引入一个唯一命名的变量(这里用 e 表示)来保存初始化程序的值,如下所示:
[数组情况]
否则,e 被定义为在声明中使用其名称而不是 [identifier-list] 时的方式。
我们使用 E 来表示表达式e的类型。
对于每个标识符,引入了一个变量,其类型为“引用到
std::tuple_element<i, E>::type”,如果其对应的初始化程序是一个左值,则为左值引用,否则为右值引用。第 i 个变量的初始化程序为
- 如果在
E的范围内通过类成员访问查找找到至少一个第一个模板参数是非类型参数的函数模板的声明,则为e.get<i>()- 否则,为
get<i>(e),其中仅通过参数相关查找查找 get,忽略非 ADL 查找。
在这些初始化表达式中,如果实体 e 的类型是左值引用(仅当 ref-qualifier 是 & 或者是 && 且初始化表达式是左值时才会发生这种情况),则e是左值,否则是 xvalue(这实际上执行了一种完美转发),i是std::size_tprvalue,<i>总是解释为模板参数列表。
英文:
The object passed to get is defined to be an xvalue in your case, it's not customer directly.
From cppreference:
> A structured binding declaration first introduces a uniquely-named variable (here denoted by e) to hold the value of the initializer, as follows:
>
> [Array case]
>
> Otherwise e is defined as if by using its name instead of [ identifier-list ] in the declaration.
> We use E to denote the type of the expression e.
> For each identifier, a variable whose type is "reference to std::tuple_element<i, E>::type" is introduced: lvalue reference if its corresponding initializer is an lvalue, rvalue reference otherwise. The initializer for the i-th variable is
>
> - e.get<i>(), if lookup for the identifier get in the scope of E by class member access lookup finds at least one declaration that is a function template whose first template parameter is a non-type parameter
> - Otherwise, get<i>(e), where get is looked up by argument-dependent lookup only, ignoring non-ADL lookup.
> In these initializer expressions, e is an lvalue if the type of the entity e is an lvalue reference (this only happens if the ref-qualifier is & or if it is && and the initializer expression is an lvalue) and an xvalue otherwise (this effectively performs a kind of perfect forwarding), i is a std::size_t prvalue, and <i> is always interpreted as a template parameter list.
答案2
得分: 3
TLDR; 基本上,get 是在 customer 的一个副本上调用的(它具有值类别 xvalue),而不是在 lvalue customer 本身上调用的。
当你使用 auto [id] = customer; 时,它捕获了 customer 在一个隐藏变量(也称为 虚拟变量)中,称为 e。然后在 e 上调用 get,这是一个副本,具有值类别 rvalue(在这种情况下具体是 xvalue),由于类型为 T 的 rvalue 无法绑定到非 const lvalue 引用的 T,所以我们会得到上述错误。
英文:
TLDR; Basically, get is called on a copy of customer(that has the value category of xvalue) and not on lvalue customer itself.
When you use auto [id] = customer;, it captures customer in a hidden variable(aka dummy variable) say e. Then the call to getis made on e which is a copy and has the value category of rvalue(xvalue to be specific in this case) and since an rvalue of type T cannot be bound to a non-const lvalue reference to T, we get the mentioned error.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论