删除左值,保留左值引用(标准类型特征可用?)

Remove rvalueness, keep lvalue references (standard type trait available?)

本文关键字:特征 类型 保留 引用 删除 标准      更新时间:2023-10-16

我正试图编写一个函数,以std::tuple的形式返回变参数包的子集。理想情况下,该函数应该没有运行时开销(没有不必要的副本),并且应该允许用户访问lvalue引用并修改它们。

应维护值类型、lvalue引用和const lvalue引用。临时引用(rvalue引用)应"转换"为值类型,以避免创建无效引用(对临时引用的引用)。

所需结果示例:

int lr = 5;
const int& clr = lr;
auto t = make_subpack_tuple(lr, clr, 5);
static_assert(is_same
<
    decltype(t), 
    std::tuple<int&, const int&, int>
>{}, "");
// Ok, modifies lr:
std::get<0>(t) = 10;
// Compile-time error, intended:
// std::get<1>(t) = 20;
// Ok, 5 was moved into the tuple:
std::get<2>(t) = 30;

不完整实施示例:

template<typename... Ts>
auto make_subpack_tuple(Ts&&... xs)
{
    return std::tuple
    <
        some_type_trait<decltype(xs)>...
    >
    (
        std::forward<decltype(xs)>(xs)...
    );
}

我想做的事情有意义吗?

是否有一种标准类型特征可以用来代替some_type_trait?还是我应该实施自己的解决方案?

您的解决方案将是

template<typename... Ts>
auto make_subpack_tuple(Ts&&... xs)
{
    return std::tuple<Ts...>(std::forward<Ts>(xs)...);
}

根据模板参数推导规则,参数包Ts...将只包含cv限定类型和lvalue。这个问题中的信息可能也很有用。

我只想指出,在实现Nick Athanasios的可折叠Op<operation>的有效版本时,我遇到了同样的非绝对问题("我认为我需要衰减右值引用并保持左值引用不变")。我遇到了这样的麻烦:

template<class Pack, class Op>
struct Foldable
{
    mystery_trait_t<Pack> value;
    const Op& op;
    template<class RhsPack>
    auto operator*(const Foldable<RhsPack, Op>& rhs) const {
        return op(static_cast<std::decay_t<Pack>>(
            (op.f)(std::move(value), std::move(rhs.value))
        ));
    }
    operator mystery_trait_t<Pack> () && {
        return std::move(value);
    }
};
template<class Pack>
auto NamedOperator::operator()(Pack&& value) const {
    return Foldable<Pack, NamedOperator>(std::forward<Pack>(value), *this);
}

(在困惑了一会儿,然后开始问一个SO问题,找到这个现有的问题/答案,并在我的mystery_trait_t实现中添加一个static_assert,以验证它从未真正用右值引用类型调用过!)事实证明,我真正需要的只是

template<class Pack, class Op>
struct Foldable
{
    Pack value;
    const Op& op;
    template<class RhsPack>
    auto operator*(const Foldable<RhsPack, Op>& rhs) const {
        return op(
            (op.f)(std::move(value), std::move(rhs.value))
        );
    }
    operator Pack () && {
        return std::move(value);
    }
};

(在Wandbox上查看我的全部代码。)

我的这个"答案"没有提供任何新的信息,但我认为分享它会很有用,因为它只是表明,即使你认为你在模板元编程方面有很大的深度,并且确信你需要这种"条件衰减"行为。。。你真的不需要它!

可能有一个必然的一般规则,编写any_template<T&&>总是一种代码气味在Vittorio最初的问题中,他实际上做了两次,尽管这两次都被decltype语法所隐藏:

some_type_trait<decltype(xs)>...  // should have been `Ts...`
std::forward<decltype(xs)>(xs)... // could equally well be `std::forward<Ts>(xs)...`