Cor*_*sto 8 c++ language-lawyer perfect-forwarding c++17 structured-bindings
我有一个结构
template <typename T>
struct Demo {
T x;
T y;
};
Run Code Online (Sandbox Code Playgroud)
我正在尝试编写一个类似于std::get元组的泛型函数,它接受一个编译时索引I并返回一个左值引用到结构的I-th成员,如果用一个左值DemoStruct<T>和一个对I第-n成员的右值引用调用它如果用rvalue调用它的结构DemoStruct<T>.
我目前的实现看起来像这样
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);
}
}
Run Code Online (Sandbox Code Playgroud)
但是,这不符合我的预期,并始终返回rvalue-reference T.
这是一个显示问题的wandbox.
返回对结构成员的引用的正确方法是什么,保留传递给函数的结构的值类别?
编辑:正如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)>);
Run Code Online (Sandbox Code Playgroud)
和
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)>);
Run Code Online (Sandbox Code Playgroud)
编译正常,即使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)))>);
Run Code Online (Sandbox Code Playgroud)
这是GCC和Clang中的语言缺陷,意图还是错误?
行为是正确的.
decltype应用于结构化绑定将返回引用的类型,对于普通结构,它是所引用的数据成员的声明类型(但使用完整对象的cv限定符进行修饰),并且对于类似元组的情况是" tuple_element返回的任何内容" 对于那个元素".这大致模拟了decltype应用于普通类成员访问的行为.
我目前无法想到除手动计算所需类型之外的任何内容,即:
using fwd_t = std::conditional_t<std::is_lvalue_reference_v<T>,
decltype(a)&,
decltype(a)>;
return std::forward<fwd_t>(a);
Run Code Online (Sandbox Code Playgroud)
这是获得所需内容的通用解决方案。它是一种变体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);
}
}
Run Code Online (Sandbox Code Playgroud)
我将其命名aliasing_forward为对std::shared_ptr.
你可以这样使用它:
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);
}
}
Run Code Online (Sandbox Code Playgroud)