【发布时间】:2017-05-27 17:37:02
【问题描述】:
我有一个结构
template <typename T>
struct Demo {
T x;
T y;
};
我正在尝试为元组编写一个类似于std::get 的通用函数,该函数采用编译时索引I 并返回对结构的I-th 成员的左值引用(如果它被调用)如果使用右值 DemoStruct<T> 调用该结构,则使用左值 DemoStruct<T> 和对结构的 I-th 成员的右值引用。
我目前的实现是这样的
template <size_t I, typename T>
constexpr decltype(auto) struct_get(T&& val) {
auto&& [a, b] = std::forward<T>(val);
if constexpr (I == 0) {
return std::forward<decltype(a)>(a);
} else {
return std::forward<decltype(b)>(b);
}
}
但是,这并没有达到我的预期,而是总是返回一个对 T 的右值引用。
Here 是一个显示问题的魔杖盒。
返回对结构成员的引用的正确方法是什么?保留传递给函数的结构的值类别?
编辑:
正如 Kinan Al Sarmini 指出的那样,auto&& [a, b] = ... 确实将a 和b 的类型推导出为非引用类型。 std::tuple 也是如此,例如两者都有
std::tuple my_tuple{std::string{"foo"}, std::string{"bar"}};
auto&& [a, b] = my_tuple;
static_assert(!std::is_reference_v<decltype(a)>);
和
std::tuple my_tuple{std::string{"foo"}, std::string{"bar"}};
auto&& [a, b] = std::move(my_tuple);
static_assert(!std::is_reference_v<decltype(a)>);
编译正常,即使std::get<0>(my_tuple) 返回引用,如
std::tuple my_tuple{3, 4};
static_assert(std::is_lvalue_reference_v<decltype(std::get<0>(my_tuple))>);
static_assert(std::is_rvalue_reference_v<decltype(std::get<0>(std::move(my_tuple)))>);
这是 GCC 和 Clang 中的语言缺陷、故意还是错误?
【问题讨论】:
-
a和b被推导出为非引用类型,这就是您收到右值引用的原因。我不确定为什么会这样。 -
我不明白你的困惑。只要
val是一个引用,其成员的任何“移动”都会对调用对象产生影响。为什么不按照std::get的示例提供另一个重载,它采用const T&而不是move。 -
@DeiDei
std::forward在提供左值引用类型时不应该返回右值引用,如果与auto&&的结构化绑定遵循常规参数推导规则,decltype(a)应该是。如果我为T const&创建另一个重载,因为它在演绎上下文中,对于非常量左值,T&&重载仍将优于T const&版本。所以我必须为所有 cv 限定版本创建重载,即T&、T const&、T volatile&、T const volatile&,以便它可以处理各种左值;这是很多代码重复。 -
您是否尝试过使用元组或手动结构化绑定?即这是特定于结构的自动结构化绑定吗?
-
@Yakk 这似乎并不特定于带有结构的结构化绑定。我已经用更多信息更新了这个问题。
标签: c++ language-lawyer c++17 perfect-forwarding structured-bindings