9

我有一个结构

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

我正在尝试编写一个类似于std::getfor tuples 的通用函数,它采用编译时索引I并返回I对结构的第 -th 成员的左值引用,如果它被调用时使用左值和对第 -th 成员DemoStruct<T>的右值引用I如果使用 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);
    }
}

但是,这并没有达到我的预期,T而是总是返回一个右值引用。

是一个显示问题的魔杖盒。

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

编辑:正如 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 中的语言缺陷、故意还是错误?

4

2 回答 2

8

行为是正确的。

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);
于 2017-05-27T19:29:05.407 回答
5

这是获得您想要的东西的一般解决方案。它是一种变体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.

你可以像这样使用它:

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);
    }
}

演示

于 2017-05-28T18:34:29.137 回答