完美转发使用结构化绑定声明的变量
Perfect forwarding of variables declared with structured binding
我有一个结构
template <typename T>
struct Demo {
T x;
T y;
};
并且我正在尝试为元组编写一个类似于 std::get
的通用函数,该元组采用编译时索引 I
和 returns 对 [= 的左值引用如果用左值 DemoStruct<T>
调用结构的第 16=] 个成员,如果用右值 DemoStruct<T>
调用结构的第 I
个成员的右值引用.
我当前的实现如下所示
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);
}
}
但是,这并没有达到我的预期,并且总是 return 是对 T
的右值引用。
Here 是一个显示问题的魔杖盒。
return 引用保留传递给函数的结构的值类别的结构成员的正确方法是什么?
编辑:
正如 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)
returns 引用,如
所示
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 中的语言缺陷、故意的还是错误?
行为正确。
decltype
应用于结构化绑定 returns 引用类型,对于普通结构,它是引用的数据成员的声明类型(但用完整的 cv 限定符修饰) object),对于类似元组的情况是 "whatever tuple_element
returned for that element"。这粗略地模拟了 decltype
应用于普通 class 成员访问的行为。
我目前想不出除了手动计算所需类型之外的任何事情,即:
using fwd_t = std::conditional_t<std::is_lvalue_reference_v<T>,
decltype(a)&,
decltype(a)>;
return std::forward<fwd_t>(a);
这是获得所需内容的通用解决方案。它是 std::forward
的变体,它允许其模板参数和函数参数具有不相关的类型,并有条件地将其函数参数转换为右值,前提是其模板参数不是左值引用。
template <typename T, typename U>
constexpr decltype(auto) aliasing_forward(U&& obj) noexcept {
if constexpr (std::is_lvalue_reference_v<T>) {
return obj;
} else {
return std::move(obj);
}
}
我将它命名为 aliasing_forward
是为了向 std::shared_ptr
的 "aliasing constructor" 致敬。
你可以这样使用它:
template <size_t I, typename T>
constexpr decltype(auto) struct_get(T&& val) {
auto&& [a, b] = val;
if constexpr (I == 0) {
return aliasing_forward<T>(a);
} else {
return aliasing_forward<T>(b);
}
}
我有一个结构
template <typename T>
struct Demo {
T x;
T y;
};
并且我正在尝试为元组编写一个类似于 std::get
的通用函数,该元组采用编译时索引 I
和 returns 对 [= 的左值引用如果用左值 DemoStruct<T>
调用结构的第 16=] 个成员,如果用右值 DemoStruct<T>
调用结构的第 I
个成员的右值引用.
我当前的实现如下所示
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);
}
}
但是,这并没有达到我的预期,并且总是 return 是对 T
的右值引用。
Here 是一个显示问题的魔杖盒。
return 引用保留传递给函数的结构的值类别的结构成员的正确方法是什么?
编辑:
正如 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)
returns 引用,如
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 中的语言缺陷、故意的还是错误?
行为正确。
decltype
应用于结构化绑定 returns 引用类型,对于普通结构,它是引用的数据成员的声明类型(但用完整的 cv 限定符修饰) object),对于类似元组的情况是 "whatever tuple_element
returned for that element"。这粗略地模拟了 decltype
应用于普通 class 成员访问的行为。
我目前想不出除了手动计算所需类型之外的任何事情,即:
using fwd_t = std::conditional_t<std::is_lvalue_reference_v<T>,
decltype(a)&,
decltype(a)>;
return std::forward<fwd_t>(a);
这是获得所需内容的通用解决方案。它是 std::forward
的变体,它允许其模板参数和函数参数具有不相关的类型,并有条件地将其函数参数转换为右值,前提是其模板参数不是左值引用。
template <typename T, typename U>
constexpr decltype(auto) aliasing_forward(U&& obj) noexcept {
if constexpr (std::is_lvalue_reference_v<T>) {
return obj;
} else {
return std::move(obj);
}
}
我将它命名为 aliasing_forward
是为了向 std::shared_ptr
的 "aliasing constructor" 致敬。
你可以这样使用它:
template <size_t I, typename T>
constexpr decltype(auto) struct_get(T&& val) {
auto&& [a, b] = val;
if constexpr (I == 0) {
return aliasing_forward<T>(a);
} else {
return aliasing_forward<T>(b);
}
}