【问题标题】:get know whether 'this' or other arguments are rvalue了解“this”或其他参数是否为右值
【发布时间】:2020-04-02 15:08:11
【问题描述】:

假设我们有 Base 类及其成员函数 Base doSomething(const Base& other)。 我想知道如何确定这个或其他对象是否是右值, 例如我需要类似的东西

Base Base::doSomething(const Base& other) {

    ...
    if(this_is_rvalue) {
        // use resources of *this
    }
    else if(other_is_rvalue) {
        // use resources of other
    }
    ...
}

我知道可能的解决方案是使用模板化朋友功能:

template<typename T1, typename T2, typename = typename std::enable_if<.....>::type>
friend Base doSomething(T1&& this_, T2&& other) {

    ...
    if(std::is_rvalue_reference<T1&&>::value) {
        // use resources of this_
        return std::move(this_);
    }
    else if(std::is_rvalue_reference<T2&&>::value) {
        // use resources of other
        return std::move(other);
    }
}

但是在我的情况下这种方法是非常不可取的

提前致谢!

【问题讨论】:

  • 抱歉,忘记添加 const,已更正
  • 那么other 再次不是右值,而是可能绑定或不绑定到右值的左值。但同样,编译器会保护你,重用资源需要对 const 引用进行非 const 访问,所以这是不可能的。 (当 const 引用指向非 const 对象时,请不要向 const_cast 提议,因为这确实是允许的,但只是邪恶的)

标签: c++ move-semantics rvalue-reference perfect-forwarding


【解决方案1】:

您可以使用 4 个重载来做到这一点:

Base Base::doSomething(Base& other) && {
     // *this is an rvalue and other is an lvalue
}

Base Base::doSomething(Base&& other) && {
    // *this and other are rvalues
}

Base Base::doSomething(Base& other) & {
    // *this and other are lvalues
}

Base Base::doSomething(Base&& other) & {
    // *this is an lvalue and other is an rvalue
}

如果您希望能够接受const Base,那么您可以制作这些模板并使用SFINAE 来确保衰减的模板类型是Base 之类的

template <typename T, std::enable_if_t<std::is_same_v<std::decay_t<T>, Base>, bool> = true>
Base Base::doSomething(T& other) && {
     // *this is an rvalue and other is an lvalue
}

Base Base::doSomething(Base&& other) && {
    // *this and other are rvalues
}

template <typename T, std::enable_if_t<std::is_same_v<std::decay_t<T>, Base>, bool> = true>
Base Base::doSomething(T& other) & {
    // *this and other are lvalues
}

Base Base::doSomething(Base&& other) & {
    // *this is an lvalue and other is an rvalue
}

另一种方法是只使用两个重载和转发引用。然后您可以检查参数的类型是否为左值引用以确定您是否具有左值或右值。这在您使用转发引用时有效,因为模板参数的类型推断为T&amp;,而不是T(如果它是右值)。这会给你类似的代码

template<typename A, typename B, typename C>
Base Base::doSomething(A&& other1, B&& other2, C&& other3) &
{
    // this is always an lvalue in this function
    if (std::is_lvalue_reference_v<A>)
        // other1 is an lvalue
    else
        // other1 is a rvalue
    if (std::is_lvalue_reference_v<B>)
        // other2 is an lvalue
    else
        // other2 is a rvalue
    if (std::is_lvalue_reference_v<C>)
        // other3 is an lvalue
    else
        // other3 is a rvalue
}

template<typename A, typename B, typename C>
Base Base::doSomething(A&& other1, B&& other2, C&& other3) &&
{
    // this is always an rvalue in this function
    if (std::is_lvalue_reference_v<A>)
        // other1 is an lvalue
    else
        // other1 is a rvalue
    if (std::is_lvalue_reference_v<B>)
        // other2 is an lvalue
    else
        // other2 is a rvalue
    if (std::is_lvalue_reference_v<C>)
        // other3 is an lvalue
    else
        // other3 is a rvalue
}

如果你需要,你可以通过添加来约束模板

std::enable_if_t<std::is_same_v<std::decay_t<A>, type_for_other1>, 
                 std::is_same_v<std::decay_t<B>, type_for_other2>,
                 std::is_same_v<std::decay_t<B>, type_for_other3>, bool> = true

到模板参数以将模板约束为您想要的参数类型。

给每个人

【讨论】:

  • 虽然我认为这是一个(或什至是)正确的解决方案,并且 C++ 没有为我们提供使其变得更容易的工具,但我想补充一点,OP 应该将这些复杂性添加到仅在必要时使用他的代码库。由于他这样做似乎是为了性能,因此只有当您测量到此功能是性能瓶颈时,它们才是必要的。请不要只添加它,因为移动语义很酷(它们当然是!)。
  • 感谢您的回答,但我想避免重载,因为可能有不止一个“其他”参数
  • > C++ 并没有为我们提供工具来显着简化它。到目前为止,我倾向于相同的答案...
  • @defo900 你必须至少有两个重载,因为这是知道*this 是左值还是右值的唯一方法。给我一些,让我看看我是否可以只用两个重载来让它工作。
  • 谢谢。简而言之 - 存在 2 个不完美的解决方案:1)有问题引用 - 模板化友元函数; 2)你的 - 成员函数的两个重载;在我的解决方案中,坏事是 - 朋友,没有会员;在您的 - 2 个重载中。两种解决方案都需要 tempates - 这对我来说也不是那么好
【解决方案2】:

这就是你区分左值this和右值this的方式:

Base Base::doSomething(const Base& other) const &
//                                        ^^^^^^^ lvalue only

Base Base::doSomething(const Base& other) &&
//                                        ^^ rvalue only

这就是您区分左值输入和右值输入的方式,通常:

Base Base::doSomething(const Base& other) {
     // use resources of *this
}

Base Base::doSomething(Base&& other) {
    // use resources of other
}

听起来你想要两者的某种组合,所以也许:

Base Base::doSomething(Base&&) const &;
Base Base::doSomething(const Base&) &&;

不过,这让我觉得有点奇怪。考虑一下你是否真的走上了正确的道路。

【讨论】:

  • 谢谢,但是重载也不是这样,我也有其他几个成员函数:other1,other2,...所以它需要很多重载。另一方面,这种方法无助于检查这是否是右值
  • @defo 我不关注。这正是如何做到的。这就是复制/移动 ctor 和分配操作的工作方式。这就是为什么在语言中引入右值引用的原因。
  • 第一个函数不检查this 是否为右值,它适用于右值和左值。可以将&amp;&amp; 添加到函数中,这将真正检查(请注意,在 OP 示例中,也有可能两次检查都失败,而在您的示例中,这种可能性不存在)。
  • @n314159 再次阅读问题。它询问参数是否是从右值初始化的。它不是询问*this 是否是右值。或者,如果是,则需要大量澄清/改写。
  • @LightnessRaceswithMonica OP有我想知道如何确定这个或其他对象是否是右值
【解决方案3】:

来自https://en.cppreference.com/w/cpp/language/member_functions

一个非静态成员函数可以在没有引用限定符的情况下声明,可以使用左值引用限定符(参数列表后面的标记 &)或右值引用限定符(参数列表后面的标记 &&)。在重载决议期间,类 X 的非静态 cv 限定成员函数被处理如下:

  • 没有引用限定符:隐式对象参数具有对 cv 限定 X 的左值引用类型,并且还允许绑定右值隐含对象参数
  • lvalue ref-qualifier:隐式对象参数的类型为 cv-qualified X 的左值引用
  • rvalue ref-qualifier:隐式对象参数的类型为 cv-qualified X 的右值引用

所以,如果你这样做:

Base Base::doSomething(const Base& other) &&;

将为this'rvalued'调用此函数

【讨论】:

  • 基于OP的例子,他们想知道输入是左值还是右值,而不是*this是不是。
  • (他们重新使用“this”关键字作为函数参数名称无济于事!)
  • 谢谢,这种方法需要 4 个重载: Base Base::doSomething(const Base& other); Base Base::doSomething(const Base& other) &&; Base Base::doSomething(Base&& other); Base Base::doSomething(Base&& other) &&;正如我在之前的 cmets 中所说,当我有 2 个参数(other1、other2)时 - 这变成了 2^n 问题
  • 为什么投反对票?答案的哪一部分是错误的?如果这是 OP 问题的最佳解决方案很难说,一旦 OP 不清楚目标,但是,正如其他解决方案所示,答案是基于 ref-qualifier。
  • @LightnessRaceswithMonica 看看标题:get know if 'this' or other arguments are rvalue and the statement if(this_is_rvalue) so, to me ,很明显OP想知道*this对象是右值还是左值。奇怪的是,您的答案恰好涉及这种二分法,那么您的 cmets 到底要指出什么?
猜你喜欢
  • 1970-01-01
  • 2010-10-05
  • 2018-10-11
  • 1970-01-01
  • 2010-09-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多