-1

Suppose we have class Base and its member function Base doSomething(const Base& other). I would like to know how to determine whether this or other object is rvalue, for example I need something like

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

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

I know that possible solution is to use templated friend function:

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

however this approach would be highly undesirable in my case

Thanks in advance !

defo900
  • 1
  • 2
  • sorry, forgot to add const, corrected – defo900 Dec 09 '19 at 13:43
  • 1
    Then `other` is again not an rvalue, but rather an lvalue that may or may not be bound to an rvalue. But again, the compiler will protect you, reusing resources would require a non-const-access to a const reference, so that won't be possible. (And please don't propose to `const_cast` when the const reference is to a non-const object, because that would be indeed allowed but just evil) – n314159 Dec 09 '19 at 13:53

3 Answers3

4

You can do this using a 4 overloads:

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
}

If you want to be able to accept const Base's then you can make these templates and use SFINAE to make sure the decayed template type is a Base like

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
}

Another way to do this is with just two overloads and forwarding references. Then you can check if the type of the parameter is an lvalue reference to determine if you have an lvalue or rvalue. This works when you use forwarding references since the type of the template parameter is deduced to be T& instead of T if it was an rvalue. That would give you code like

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
}

And then if you need to you can constrain the template by adding

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

To the template parameters to constrain the template to the types you want for the parameters.

to each of

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • While I think this is a (or maybe even the) correct solution and C++ doesn't give us the tools to make it significantly easier, I would like to add, that OP should add these complications to his code base only if they are necessary. Since it seems he does this for performance, they are necessary only if you have measured that this function is a performance bottle neck. Please don't just add it because move-semantics are cool (what they certainly are!). – n314159 Dec 09 '19 at 14:02
  • thank you for answer , but I would like to avoid overloads since there may be more then one 'other' argument – defo900 Dec 09 '19 at 14:03
  • > C++ doesn't give us the tools to make it significantly easier. So far I incline to the same answer... – defo900 Dec 09 '19 at 14:10
  • @defo900 You're going to have to have at least two overloads since that is the only way to know if `*this` is an lvalue or rvalue. Give me a few and let me see if I can get this to work with just two overloads. – NathanOliver Dec 09 '19 at 14:14
  • thank you. In short - 2 not perfect solutions are present: 1) quoted in question - templated friend function; 2) your - two overloads of member function; In my solution bad thing is - friend, no member; in your - 2 overloads. Both solutions require tempates - this is also not so good for in my case – defo900 Dec 09 '19 at 14:43
  • I wonder if it is possible to make such check in member not-templated function... – defo900 Dec 09 '19 at 14:53
  • @defo900 It's not possible. If you take in a `const&` for instance there is no way in the function to know if you got an lavlue or rvalue. – NathanOliver Dec 09 '19 at 14:54
  • ok, and what about 'this' ? I tried 'std::is_rvalue_reference - doesn't work – defo900 Dec 09 '19 at 14:59
  • @defo900 That won't work either. Inside the function `*this` is an lvalue. The only way to know if this is an lvalue or not is to overload on the reference qualification. – NathanOliver Dec 09 '19 at 15:01
1

This is how you discriminate between lvalue this and rvalue this:

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

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

This is how you discriminate between lvalue inputs and rvalue inputs, generally:

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

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

It sounds like you wanted some combination of the two, so perhaps:

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

This strikes me as kind of strange, though. Consider whether you're really going down the proper path.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • thanks, however overload is not the case also, I have member functions with several others as well: other1, other2,... So it would require a lot of overloads. On the other hand this approach doesn't help to check whether this is rvalue – defo900 Dec 09 '19 at 13:48
  • 1
    @defo I don't follow. This is exactly how to do it. It's how copy/move ctors and assignment ops work. This is literally why rvalue references were introduced to the language. – Lightness Races in Orbit Dec 09 '19 at 13:50
  • The first function does not check whether `this` is an rvalue, it works for both rvalues and lvalues. One could add a `&&` to the function and that would really check that (note that in OPs example, there is also the possibility to fail both checks, where jn your example this possibility does not exist). – n314159 Dec 09 '19 at 13:50
  • @n314159 Read the question again. It is asking whether the argument was initialised from an rvalue. It is not asking about whether `*this` is an rvalue. Or, if it is, it needs a lot of clarifying/rephrasing. – Lightness Races in Orbit Dec 09 '19 at 13:51
  • @LightnessRaceswithMonica The OP has *I would like to know how to determine whether this or other object is rvalue* – NathanOliver Dec 09 '19 at 13:54
  • @NathanOliver-ReinstateMonica They named one of the arguments `this` :P – Lightness Races in Orbit Dec 09 '19 at 14:03
-1

From https://en.cppreference.com/w/cpp/language/member_functions

A non-static member function can be declared with no ref-qualifier, with an lvalue ref-qualifier (the token & after the parameter list) or the rvalue ref-qualifier (the token && after the parameter list). During overload resolution, non-static cv-qualified member function of class X is treated as follows:

  • no ref-qualifier: the implicit object parameter has type lvalue reference to cv-qualified X and is additionally allowed to bind rvalue implied object argument
  • lvalue ref-qualifier: the implicit object parameter has type lvalue reference to cv-qualified X
  • rvalue ref-qualifier: the implicit object parameter has type rvalue reference to cv-qualified X

So, if you do:

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

This function will be called for this 'rvalued'

Community
  • 1
  • 1
Amadeus
  • 10,199
  • 3
  • 25
  • 31
  • Based on the OP's example, they want to know whether the input was lvalue/rvalue, not whether `*this` was. – Lightness Races in Orbit Dec 09 '19 at 13:50
  • (Doesn't help that they re-used the "this" keyword as a function argument name!) – Lightness Races in Orbit Dec 09 '19 at 13:51
  • thanks, this approach requires 4 overloads: Base Base::doSomething(const Base& other); Base Base::doSomething(const Base& other) &&; Base Base::doSomething(Base&& other); Base Base::doSomething(Base&& other) &&; as I said in previous comments, when I have 2 arguments (other1, other2) - this becomes 2^n problem – defo900 Dec 09 '19 at 13:52
  • Why the downvote? Which part of the answer is wrong? If this is the best solution to OP problem is hard to say, once the OP is not clear about the objectives, but, as the other solutions had shown, the answer is based on ref-qualifier. – Amadeus Dec 09 '19 at 15:50
  • @LightnessRaceswithMonica look at the title: _get know whether **'this'** or other arguments are rvalue_ and the statement `if(this_is_rvalue)` so, to me, it is very clear the OP wants to know if `*this` object is rvalue or lvalue. Strange that your answer deals exactly with this dichotomy, so what exactly are you pointing out with your comments? – Amadeus Dec 09 '19 at 15:54
  • @defo900 You have 2 conditions, so you got 4 paths to follow. You can solve this using an approach like `if ... then ... else` or can use an overload to simulate it. There is no magic here. Another way is using templates, but you still had those paths to follow. By the way, I cant provide solutions about what is not written. There is nothing in your question telling about 2 or more arguments, neither about the restriction of the solution – Amadeus Dec 09 '19 at 15:57
  • @Amadeus You're a bit late. I edited my answer, literally hours ago, in order to cover both possibilities and after the question had been clarified. This all occurred after the comments. – Lightness Races in Orbit Dec 09 '19 at 15:59
  • @LightnessRaceswithMonica yeah, I have to work, but the comments persisted – Amadeus Dec 09 '19 at 16:00
  • I did not sign a contract with you requiring me to immediately delete them, sorry. – Lightness Races in Orbit Dec 09 '19 at 16:00
  • @LightnessRaceswithMonica neither me to immediatly answer you – Amadeus Dec 09 '19 at 16:01
  • @Amadeus I did not complain about you not immediately answering :) I merely explained the situation to you. You're welcome. Have a good day. – Lightness Races in Orbit Dec 09 '19 at 16:04
  • @LightnessRaceswithMonica what Im complaing is about an aggressive message _I did not sign a contract with you requiring me to immediately delete them, sorry._ due an comment answer that still persist here. – Amadeus Dec 09 '19 at 16:06
  • Oh just stop would you – Lightness Races in Orbit Dec 09 '19 at 16:14