为 class 提供类似元组的结构化绑定访问
Providing tuple-like structured binding access for a class
我正在尝试为 class 支持类似元组的结构化绑定访问。为简单起见,我将在 post 的其余部分使用以下 class:
struct Test
{
int v = 42;
};
(我知道这个 class 支持开箱即用的结构化绑定,但我们假设它不支持。)
到enable tuple-like access到Test
的成员,我们必须特化std::tuple_size
和std::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<i>
或 Test
命名空间中的函数 get<i>(Test)
。让我们实现后者:
template<std::size_t I>
int get(Test t)
{
return t.v;
}
这行得通。但是,我想 return 引用 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):
binding reference of type ‘std::tuple_element<0, Test>::type& {aka int&}’ to ‘const int’ discards qualifiers
因此似乎为结构化绑定选择了 get<i>(const Test&)
重载。由于此重载 return 是 const int&
,并且 v
的行为类似于对 int
的非 const
引用,因此代码无法编译。
然而,根据 this,行 auto [v] = test;
应该大致等同于
auto e = test;
std::tuple_element<0, Test>::type& v = get<0>(e)
可以工作,因为它使用 get<i>(Test&)
重载。
关于为什么我的 get
实现不适用于结构化绑定有什么想法吗?
问题是 auto [v]
是一个非引用声明,所以 test
被复制并且 test
的副本作为 xvalue 传递给 get
。
所以需要添加一个右值限定get:
template<std::size_t I>
int&& get(Test&& t)
{
return std::move(t.v);
}
我正在尝试为 class 支持类似元组的结构化绑定访问。为简单起见,我将在 post 的其余部分使用以下 class:
struct Test
{
int v = 42;
};
(我知道这个 class 支持开箱即用的结构化绑定,但我们假设它不支持。)
到enable tuple-like access到Test
的成员,我们必须特化std::tuple_size
和std::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<i>
或 Test
命名空间中的函数 get<i>(Test)
。让我们实现后者:
template<std::size_t I>
int get(Test t)
{
return t.v;
}
这行得通。但是,我想 return 引用 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):
binding reference of type ‘std::tuple_element<0, Test>::type& {aka int&}’ to ‘const int’ discards qualifiers
因此似乎为结构化绑定选择了 get<i>(const Test&)
重载。由于此重载 return 是 const int&
,并且 v
的行为类似于对 int
的非 const
引用,因此代码无法编译。
然而,根据 this,行 auto [v] = test;
应该大致等同于
auto e = test;
std::tuple_element<0, Test>::type& v = get<0>(e)
可以工作,因为它使用 get<i>(Test&)
重载。
关于为什么我的 get
实现不适用于结构化绑定有什么想法吗?
问题是 auto [v]
是一个非引用声明,所以 test
被复制并且 test
的副本作为 xvalue 传递给 get
。
所以需要添加一个右值限定get:
template<std::size_t I>
int&& get(Test&& t)
{
return std::move(t.v);
}