1

I read about Lvalue-to-rvalue conversion and Comparison operators

This quote from Comparison operators :

after the application of the lvalue-to-rvalue, array-to-pointer and function-to-pointer standard conversions. The comparison is deprecated if both operands have array type prior to the application of these conversions. (since C++20)

This quote from Lvalue-to-rvalue conversion :

An lvalue (until C++11)A glvalue (since C++11) of any non-function, non-array type T can be implicitly converted to an rvalue (until C++11)a prvalue (since C++11):

If T is not a class type, the type of the rvalue (until C++11)prvalue (since C++11) is the cv-unqualified version of T. Otherwise, the type of the rvalue (until C++11)prvalue (since C++11) is T. If an lvalue-to-rvalue conversion from an incomplete type is required by a program, that program is ill-formed.

I just want to make sure that I understand how this conversion works right ( My Understand ) :

Example 1 :

#include <iostream>
using namespace std;
int main() {
    int x=9;
    if(x == 0){
     cout<<"Okay"<<endl;    
    }
    return 0;
}

Example 2 :

#include <iostream>
using namespace std;
int main() {
    int x=9;
    if(0 == x){
     cout<<"Okay"<<endl;    
    }
    return 0;
}

I concluded two points :

The point number 1 : in Example 1 Lvalue-to-rvalue conversion is applied to the two operands ( x and 0 ) of the operator == before the The comparison and that is why the Example 2 is also compiled and run .

The point number 2 : Lvalue-to-rvalue conversion is never applied to class type . ( is that the exact meaning of this section ( because the english is not my native language so I want to make sure that I understand this section totally right ) ? : )

If T is not a class type, the type of the rvalue (until C++11)prvalue (since C++11) is the cv-unqualified version of T. Otherwise, the type of the rvalue (until C++11)prvalue (since C++11) is T. If an lvalue-to-rvalue conversion from an incomplete type is required by a program, that program is ill-formed.

So at the end is my conclusion ( the two points ) totally right ?

Edit : enter image description here

here the Inside class definition of operators and the Outside class definition for example for equal to Operator ( == ) the Inside class definition is ( for class T )
bool T::operator==(const T2& b) const;
and the Outside class definition is bool operator==(const T& a, const T2& b); So I have four questions for you @Caleth you in your last comment ( under your answer ) " bool operator==(int &, Foo&) takes lvalues, whereas bool operator(int, Bar) takes rvalues "

question number 1 - what I understand from your comment is Foo is a class and Bar is also a class is that right ?

you said in another comment " for the built-in operators, i.e. both operands are of scalar type, then lvalue-to-rvalue conversion is done because all built-in operators are by-value. For user defined operators, it is or isn't done depending on the declared type of the parameter "

question number 2 - " the built-in operators, i.e. both operands are of scalar type, then lvalue-to-rvalue conversion is done because all built-in operators are by-value. " I don not understand you said " because all built-in operators are by-value. " when we take a look on the table ( the photo ) we can see that the Outside class definition is bool operator==(const T& a, const T2& b);

Is the Outside class definition the definition of built-in operators ?

question number 3 - if the answer of the question number 2 is YES then why is the lvalue-to-rvalue conversion done when we use built-in operators ( if the parameter in the definition of the built-in operators is not by - value ) and if the answer of the question number 2 is NO then What is the difference between Outside class definition and the definition of built-in operators ?

you said " For user defined operators, it is or isn't done depending on the declared type of the parameter " and you explain this by said " bool operator==(int &, Foo&) takes lvalues, whereas bool operator(int, Bar) takes rvalues " If we apply your words

#include <iostream>
using namespace std;
class test {
public:
    test() {
        cout << "test()" << endl;
    }
    test(test&& p) {
        cout << "test( test&& p )" << endl;
    }
    test(const test& p) {
        cout << "test( const test& p )" << endl;
    }
    test& operator==(test p) {
        return p;
    }
};
int main() {
    test p;
    test o;
    p == o;
    return 0;
} 

the output of this code is :

test() ------- for test p
test() ------- for test o
test( const test& p ) -------- for operator ==

so here the parameter of the overloaded operator == is test p ( is by - value ) and the lvalue-to-rvalue conversion does not applied

question number 4 - does not that make the lvalue-to-rvalue conversion does not applied when you use a class type ( any class type ) with an overloaded operator ( any overloaded operator ) ( even if the parameter of the overloaded operator is by - value or not ) ( there is no case that the lvalue-to-rvalue conversion is applied when you use a class type ( any class type ) with an overloaded operator ( any overloaded operator ) ) ?

you said "There are user-defined operators that require lvalue-to-rvalue conversions, e.g. bool operator==(int, Bar), and ones that don't, e.g. bool operator==(int &, Foo&)."

question number 5 - you said " bool operator==(int, Bar) requires lvalue-to-rvalue conversions " here bool operator==(int, Bar) The lvalue-to-rvalue conversion happens with the parameter int and The lvalue-to-rvalue conversion does not happen with the parameter Bar , right ?

sorry for bothering you @Caleth

f877576
  • 447
  • 2
  • 7

1 Answers1

2

in Example 1 Lvalue-to-rvalue conversion is applied to the two operands ( x and 0 )

No. The expression x is an lvalue, so it is converted. The expression 0 is already an rvalue, so it is not.

Lvalue-to-rvalue conversion is never applied to class type .

No. There is a sentence starting "Otherwise" after the non-class type case, there is a different rule for class types.

Caleth
  • 52,200
  • 2
  • 44
  • 75
  • you said " No. The expression x is an lvalue, so it is converted. The expression 0 is already an rvalue, so it is not. " you means Lvalue-to-rvalue conversion is applied to `x` by " so it is converted " , right ? – f877576 May 31 '23 at 15:59
  • @f877576 yes, if where it is used needs an rvalue. In the opposite case, you can't get an lvalue out of an rvalue. – Caleth May 31 '23 at 16:01
  • and you said "No. There is a sentence starting "Otherwise" after the non-class type case, there is a different rule for class types." here is the sentence that you mean `Otherwise, the type of the rvalue (until C++11)prvalue (since C++11) is T.` .`T` will be `T` is not that mean no change will happen ( So Lvalue-to-rvalue conversion does not applied ) ? – f877576 May 31 '23 at 16:07
  • ??????????????????? – f877576 May 31 '23 at 23:17
  • No, type is different to value category. what it means is that in `const std::string str = "blah"; str + str;`, the type is `const string` whereas in `const int i = 1; i + i;` the type is `int`. – Caleth Jun 01 '23 at 08:13
  • this question is about Comparison operators not any other operator and when did you get const ( in const string ) ? cv-unqualified version of T. Otherwise, the type of the rvalue (until C++11)prvalue (since C++11) is T ( Otherwise rvalue prvalue is T not cv-unqualified version of T ) – f877576 Jun 01 '23 at 22:24
  • @f877576 `std::string` is a class type, so it's the otherwise part. And it doesn't change for any other operator – Caleth Jun 02 '23 at 08:00
  • Is the rule of the Lvalue-to-rvalue conversion the non-class type will be changed to rvalue cv-unqualified version of T and the class type will be changed to rvalue of T ( but not cv-unqualified version ) ? ( Is my understanding of the Lvalue-to-rvalue conversion completely correct now ? ) @Caleth – f877576 Jun 04 '23 at 13:54
  • 1
    @f877576 yes, class types keep the cv qualification – Caleth Jun 13 '23 at 07:33
  • I'm sorry for bothering you.my last question is – f877576 Jun 15 '23 at 16:11
  • The Lvalue-to-rvalue conversion is applied to non class types when I use them with operators ( any operator ) ( only if the non class type is already an lvalue ) on the other hand The Lvalue-to-rvalue conversion is not applied to class types when I use them with operators ( any operator ) ( even if the class type is already an lvalue ) ( of course we must overload any operator before we use it with a class type ) ,right ? @Caleth – f877576 Jun 16 '23 at 07:31
  • And May I ask you to edit this section in your answer "No.The expression x is an lvalue,so it is converted. The expression 0 is already an rvalue, so it is not."to"No.The expression x is an lvalue,so it is converted by The Lvalue-to-rvalue conversion.The expression 0 is already an rvalue,so it is not.",please? @Caleth – f877576 Jun 16 '23 at 07:32
  • Please answer me @Caleth – f877576 Jun 21 '23 at 22:25
  • @f877576 for the built-in operators, i.e. both operands are of scalar type, then lvalue-to-rvalue conversion is done because all built-in operators are by-value. For user defined operators, it is or isn't done depending on the declared type of the parameter – Caleth Jun 21 '23 at 22:37
  • Can you explain this " For user defined operators, it is or isn't done depending on the declared type of the parameter " ? @Caleth – f877576 Jun 22 '23 at 01:10
  • @f877576 `bool operator==(int &, Foo&)` takes lvalues, whereas `bool operator(int, Bar)` takes rvalues – Caleth Jun 22 '23 at 21:21
  • your last answer made me more confused I edited my question and add 4 question for you and I mentioned your name and I am sorry for bothering you @Caleth – f877576 Jun 23 '23 at 04:33
  • 1
    The table in the question is unfortunately not very enlightening when it comes down to language lawyer questions. The actual comparison operators used for scalar types, as in `x == 0` in your example code, are the hypothetical overloads listed far down the page in a paragraph starting with ["In overload resolution against user-defined operators"](https://en.cppreference.com/w/cpp/language/operator_comparison). As you can see, these take the arguments by value and require the lvalue-to-rvalue conversion. Therefore, `x` undergoes the conversion, but `0` need not because it is already an rvalue. – j6t Jun 23 '23 at 05:49
  • 1
    @f877576 that table on cppreference describes user defined operators, and uses const T & because it's a good idea to, not because the language requires it. – Caleth Jun 27 '23 at 07:34
  • Foo and Bar are some user defined type. They could be pointer-to-class or array-of-class etc – Caleth Jun 27 '23 at 07:36
  • You Said you said " For user defined operators, it is or isn't done depending on the declared type of the parameter " and you explain this by said " bool operator==(int &, Foo&) takes lvalues, whereas bool operator(int, Bar) takes rvalues " Can you answer the question number 4 ( take the code I edited consederd ) ? @Caleth – f877576 Jun 28 '23 at 06:22
  • @f877576 There are user-defined operators that require lvalue-to-rvalue conversions, e.g. `bool operator==(int, Bar)`, and ones that don't, e.g. `bool operator==(int &, Foo&)`. In your question 3, the fact that it calls a constructor *at all* indicates that lvalue-to-rvalue conversion is taking place – Caleth Jun 28 '23 at 20:57
  • I don't understand this section of your comment "In your question 3, the fact that it calls a constructor at all indicates that lvalue-to-rvalue conversion is taking place" ( sorry but english is not my native language ) can you please answer the questions I have edited ( I edited one more question ( question number 5 ) ) ( you answered the question number one so you can start with question number 2 if you want but if you will start with question number 2 let me know in your comment ) with a very simple english and please explain the section of your comment that I don't understand @Caleth – f877576 Jul 02 '23 at 07:13
  • Please answer the questions in order ( I am very sorry for bothering you ) @Caleth – f877576 Jul 02 '23 at 07:15
  • 1
    If the operator accepts paramters by value, whenever you use an lvalue expression, there needs to be lvalue-to-rvalue conversion, which is copy initialising the parameter object from the argument. If you pass an prvalue, it isn't converted, the temporary is materialised into the parameter object – Caleth Jul 03 '23 at 08:12
  • That isn't different for scalar types using built-in operators, or user-defined types or functions – Caleth Jul 03 '23 at 08:18
  • does not the lvalue-to-rvalue conversion create a new object ( new temporary object ( rvalue ) ) ( if the lvalue-to-rvalue conversion happens to class type ) ? @Caleth – f877576 Jul 03 '23 at 10:50
  • 1
    @f877576 the purpose of the conversion is to create a new object. That's the same for scalar types, class types and pointer types – Caleth Jul 03 '23 at 12:43
  • you said "If the operator accepts paramters by value, whenever you use an lvalue expression, there needs to be lvalue-to-rvalue conversion" and you also said " the purpose of the conversion is to create a new object. That's the same for scalar types, class types and pointer types " then why the third output of my code ( the code that I edited ) is `test( const test& p )` and it is not `test( test&& p )` ( does the lvalue-to-rvalue conversion happen in this case ? if your answer is ( yes ) prove it ? ) @Caleth – f877576 Jul 06 '23 at 08:48
  • Because the copy constructor *is* the lvalue to rvalue conversion. It takes the lvalue and returns an rvalue – Caleth Jul 06 '23 at 18:35
  • constructor does not return anything @Caleth – f877576 Jul 09 '23 at 12:02
  • Does the lvalue-to-rvalue conversion happen in this case ( the code that I edited ) ? @Caleth – f877576 Jul 09 '23 at 12:32
  • @f877576 It is very simple: if you need an rvalue, but provide an lvalue, that value undergoes lvalue-to-rvalue conversion. – Caleth Jul 10 '23 at 07:17
  • if what you said is a general rule " It is very simple: if you need an rvalue, but provide an lvalue, that value undergoes lvalue-to-rvalue conversion " then why if the parameter of the functhion is a reference to an rvalue ( test && ) and the given parameter is a lvaue the lvalue-to-rvalue conversion does not happen and an error happens ? @Caleth – f877576 Jul 30 '23 at 14:23
  • @f877576 Because the rules of the language say that is an error. – Caleth Jul 31 '23 at 07:28
  • So this sentence " if you need an rvalue, but provide an lvalue, that value undergoes lvalue-to-rvalue conversion " is not a general rule , right ? @Caleth – f877576 Aug 20 '23 at 23:31
  • @f877576 references have rules about what they can bind to. So what I said was only complete in the context of values, not references – Caleth Aug 21 '23 at 17:24