【问题标题】:Remove rvalueness, keep lvalue references (standard type trait available?)删除右值,保留左值引用(标准类型特征可用?)
【发布时间】:2016-01-30 00:19:15
【问题描述】:

我正在尝试编写一个函数,它以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?还是我应该实施自己的解决方案?

【问题讨论】:

  • 我只是好奇。你为什么有兴趣做这件事?你在解决什么问题?
  • 我正在实现类似于 static_for 的东西,它在具有用户指定的数量的异构值上执行可调用对象,并且还允许用户在编译时检索当前迭代次数并中断/continue (exit early) 在编译时使用static_if。部分实现需要将可变参数包的第一个 N 参数传递给另一个内部函数,我试图通过定义一些可变参数包操作函数来概括这一点。除了nth&lt;I&gt;,我还需要subpack&lt;I, J&gt; 来完全概括这种行为
  • 通过用户指定的数量,我的意思是static_for 迭代N 组中的异构值(其中N 是用户指定的模板参数)。代表static_for 主体的可调用对象需要具有相同数量的operator()。此外,我正在为一个开源 C++14 通用库 (vrm_core) 做这一切,这是为了娱乐和学习目的而编写的。希望能回答你的问题:)
  • 不要使用decltype(xs)。类型包Ts... 正是您所需要的。所以试试std::tuple&lt;Ts...&gt;
  • @AndreyNasonov:工作完美,不知道我是怎么想到的。谢谢!您能否再次发布此解决方案作为答案,以便我接受?

标签: c++ c++14 rvalue-reference typetraits perfect-forwarding


【解决方案1】:

您的解决方案将是

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

根据template argument deduction rules,参数包Ts... 将仅包含 cv 限定类型和左值。 this question 中的信息也可能有用。

【讨论】:

    【解决方案2】:

    我想插一句,在实现Nick Athanasios's foldable Op&lt;operation&gt; 的有效版本时,我遇到了同样的问题(“我认为我需要衰减右值引用并保持左值引用不变”) .我遇到了这个烂摊子:

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

    (See my whole code on Wandbox.)

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

    可能有一个推论的一般规则,即 any_template&lt;T&amp;&amp;&gt; 总是代码味道。在 Vittorio 的原始问题中,他有效地这样做了两次,尽管两次都被 decltype 语法隐藏了:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-24
      • 2022-08-17
      • 2015-06-25
      相关资源
      最近更新 更多