完美转发使用结构化绑定声明的变量

时间:2017-05-27 17:37:02

标签: c++ language-lawyer c++17 perfect-forwarding structured-bindings

我有一个结构

template <typename T>
struct Demo {
    T x;
    T y;
};

我正在尝试编写一个类似于std::get的泛型函数,用于获取编译时索引I的元组,并返回一个左值引用到I成员如果使用左值DemoStruct<T>调用结构,并且如果使用右值I调用结构的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);
    }
}

但是,这不符合我的预期,并且总是返回对T的rvalue-reference。

Here是一个显示问题的wandbox。

返回对结构成员的引用的正确方法是什么,保留传递给函数的结构的值类别?

编辑: 正如Kinan Al Sarmini指出的那样,auto&& [a, b] = ...确实将ab的类型推断为非引用类型。对于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中的语言缺陷,意图还是错误?

2 个答案:

答案 0 :(得分:7)

行为是正确的。

应用于结构化绑定的

decltype返回引用的类型,对于普通结构,它是所引用的数据成员的声明类型(但是用完整对象的cv限定符修饰),并且类似于元组的情况是&#34;无论tuple_element返回该元素&#34;。这粗略地模拟了应用于普通类成员访问的decltype的行为。

除了手动计算所需类型之外,我目前无法想到任何其他内容,即:

using fwd_t = std::conditional_t<std::is_lvalue_reference_v<T>,
                                 decltype(a)&,
                                 decltype(a)>;
return std::forward<fwd_t>(a);

答案 1 :(得分:4)

这是获得您想要的一般解决方案。它是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,作为对#34;别名构造函数&#34;的点头。 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);
    }
}

DEMO