【问题标题】:Providing tuple-like structured binding access for a class为类提供类似元组的结构化绑定访问
【发布时间】:2017-08-15 18:17:51
【问题描述】:

我正在尝试支持类的类似元组的结构化绑定访问。为简单起见,我将在本文的其余部分使用以下类:

struct Test
{
    int v = 42;
};

(我知道这个类支持开箱即用的结构化绑定,但我们假设它不支持。)

enable tuple-like accessTest的成员,我们必须专门化std::tuple_sizestd::tuple_element

namespace std
{

template<>
struct tuple_size<Test>
{
    static const std::size_t value = 1;
};

template<std::size_t I>
struct tuple_element<I, Test>
{
    using type = int;
};

}

我们需要的最后一部分是Test::get&lt;i&gt;Test 命名空间中的函数get&lt;i&gt;(Test)。让我们实现后者:

template<std::size_t I>
int get(Test t)
{
    return t.v;
}

这行得通。但是,我想返回对Test 成员的引用,就像std::get(std::tuple) 一样。因此,我实现get如下:

template<std::size_t I>
int& get(Test& t)
{
    return t.v;
}

template<std::size_t I>
const int& get(const Test& t)
{
    return t.v;
}

但是在这个版本中,下面的代码

auto test = Test{};
auto [v] = test;

产生错误(GCC 7.1):

类型‘std::tuple_element::type& {aka int&}’到‘const int’的绑定引用丢弃了限定符

因此,似乎为结构化绑定选择了 get&lt;i&gt;(const Test&amp;) 重载。由于此重载返回一个const int&amp;,而v 的行为类似于对int 的非const 引用,因此代码无法编译。

然而,根据thisauto [v] = test; 应该大致相当于

auto e = test;
std::tuple_element<0, Test>::type& v = get<0>(e)

哪个确实工作,因为它使用get&lt;i&gt;(Test&amp;) 重载。

关于为什么我的get 实现不适用于结构化绑定的任何想法?

【问题讨论】:

  • 您忘记了“在这些初始化表达式中,如果实体 e 的类型是左值引用,则 e 是左值,否则为 xvalue”。
  • @Barry 引用的类型由初始化器的值类别确定,它是一个左值(因为它最终使用const &amp; 重载)。不确定您希望如何诊断...
  • @T.C.是的,在你回复之前删除了我的评论:)

标签: c++ c++17 structured-bindings


【解决方案1】:

问题在于auto [v] 是一个非引用声明,所以test 被复制,test 的副本作为xvalue 传递给get

所以你需要添加一个右值限定的get:

template<std::size_t I>
int&& get(Test&& t)
{
    return std::move(t.v);
}

【讨论】:

  • 这似乎确实解决了问题。但是,如果绑定了多个值并且在test 的同一个副本上多次调用get,会发生什么情况?在我看来,由于副本是作为右值引用传递的,因此第二次调用 get 时可能会导致未定义的行为。
  • @Job get 将在同一个对象上被多次调用,但 I 的值不同。您有责任确保这些调用不会发生冲突。
  • 我明白了,虽然它对我来说似乎很容易出错。这是否记录在标准中的某个地方?此外,是否有任何理由不简单地将副本作为左值引用传递?
  • @Job 不,标准并没有将其称为潜在的陷阱;我认为大多数人会考虑使用get&lt;&gt; 来修改指定元素以外的元素,无论如何都要被破坏。您的第二个问题是一个非常好的问题(可能值得单独提出一个 SO 问题);我能想到的是,作者允许右值get 与左值get 具有不同(更有效)的行为。也许get 涉及计算,而右值get 可以返回实现该计算的纯右值。不过,我想不出一个具体的例子。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-10-16
  • 1970-01-01
  • 1970-01-01
  • 2019-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多