Что такое R-значение в С++?
Может кто-нибудь объяснить или указать мне какое-то объяснение того, что такое R-Value? Я не совсем уверен, что это такое, и мой проект должен включить его. Вот демонстрация того, что такое R-значение (первая часть - r_string.hpp):
#include <algorithm>
#include <iostream>
template <typename CHAR_T = char>
class basic_rstring {
public:
typedef CHAR_T value_type;
typedef CHAR_T* pointer_type;
typedef CHAR_T const* pointer_const_type;
private:
pointer_type _data;
std::size_t _length;
public:
basic_rstring() : _data(nullptr), _length(0)
{
std::cout << "Default ctor\n";
}
basic_rstring( pointer_const_type s )
: _data( nullptr )
, _length( 0 )
{
std::cout << "Literal ctor: " << s << std::endl;
_length = strlen( s );
_data = new value_type[ _length + 1 ];
std::copy( s, s + _length + 1, _data );
}
basic_rstring( basic_rstring const& s )
: _data( nullptr )
, _length( s._length )
{
std::cout << "Copy ctor: " << s.c_str() << std::endl;
_data = new value_type [ _length + 1 ];
std::copy( s._data, s._data + s._length + 1, _data );
}
basic_rstring( basic_rstring && s ) //move constructor
: _data( s._data )
, _length( s._length )
{
std::cout << "Move ctor: " << s.c_str() << std::endl;
s._data = nullptr;
s._length = 0;
}
~basic_rstring()
{
if( _data )
std::cout << "dtor: " << _data << "\n";
else
std::cout << "NULL dtor\n";
delete [] _data;
}
basic_rstring& operator = ( basic_rstring const& s );
basic_rstring& operator = ( basic_rstring && s )
{
std::cout << "RValue assignment: " << s.c_str();
if( _data )
std::cout << " deleting...." << std::endl;
else
std::cout << " no delete..." << std::endl;
delete [] _data;
_data = s._data;
s._data = nullptr;
_length = s._length;
s._length = 0;
return *this;
}
pointer_const_type c_str() const { return _data; }
};
template <typename CHAR_T>
basic_rstring<CHAR_T>& basic_rstring<CHAR_T>::operator = ( basic_rstring const& s )
{
std::cout << "Copy assignment: " << s.c_str() << std::endl;
pointer_type newData = new value_type [ s._length + 1 ];
std::copy( s._data, s._data + s._length + 1, newData );
_length = s._length;
delete [] _data;
_data = newData;
return *this;
}
typedef basic_rstring<char> String;
typedef basic_rstring<wchar_t> wString;
#define _SCL_SECURE_NO_WARNINGS
#include "Rstring.hpp"
using namespace std;
#define BOOST_TEST_MODULE move_test
#include <boost/test/unit_test.hpp>
template <typename T_>
void old_swap( T_& a, T_&b )
{
T_ hold = a;
a = b;
b = hold;
}
BOOST_AUTO_TEST_CASE( stuff )
{
String s("Bert");
String t("Ernie");
cout << "Old swap" << endl;
old_swap(s,t);
BOOST_CHECK( !strcmp( "Bert", t.c_str() ) );
BOOST_CHECK( !strcmp( "Ernie", s.c_str() ) );
cout << "New swap" << endl;
swap(s,t);
BOOST_CHECK( !strcmp( "Bert", s.c_str() ) );
BOOST_CHECK( !strcmp( "Ernie", t.c_str() ) );
cout << "\nDone." << endl;
}
Ответы
Ответ 1
"Может кто-нибудь объяснить или указать мне какое-то объяснение, что такое R-Value? Я не уверен, что это такое"
Термин lvalue первоначально ссылался на выражение, которое может быть левой частью задания. Соответственно, rvalue (хотя, насколько я помню, этот термин не использовался стандартом C89), изначально было просто противоположным: выражение, которое не могло быть левой частью задания, но это могло бы только правая сторона.
С++ 11 усложнил это, добавив еще несколько нюансных терминов, но давайте сосредоточимся на значениях С++ 03.
Например, если у вас есть
int x;
тогда присваивание x = 42
ОК, поэтому x
является выражением lvalue.
В качестве встречного примера, присваивание x+0 = 42
не является ОК, поэтому x+0
является выражением rvalue.
А также выражение 2+2
, это выражение rvalue.
Итак, если требование состоит в том, что ваша программа должна включать rvalue, тогда просто напишите 2+2
или, например. (более продвинутый) 6*7
, в main
.
У оригинала C не было const
. В С++ с const
вы должны игнорировать const
для обозначения выражения как lvalue или rvalue. Критической точкой является то, является ли гарантированное выражение ссылкой на объект в памяти, объект с адресом: если это так, то выражение является lvalue.
Тип ссылки подразумевает lvalue, потому что выражение ссылочного типа обязательно ссылается на объект с адресом памяти, то есть это выражение является lvalue.
Однако, кроме ссылок, нет связи между типом и lvalue/rvalue. Например, оба x
и x+0
являются выражениями типа int
, и они дают то же значение int
. Но первое является выражением lvalue, в то время как последнее является выражением rvalue.
Как правило, если вы можете применить встроенный оператор адреса, то это выражение lvalue, и в противном случае это выражение rvalue.
Ответ 2
Термин rvalue вытекает из его исторического контекста - это то, что может быть только в правой части задания, в отличие от lvalue который может идти по левой стороне задания. Таким образом, именованная переменная (например, x
) является значением lvalue, но буквальное целое число (например, 42
) является rvalue.
Однако в современном С++ он более тонкий, чем этот.
В С++ rvalue является неназванным объектом или членом такого объекта, который не является ссылкой.
Некоторые примеры:
std::string s;
std::string foo(){ return "foo";}
struct X {
std::string s;
};
std::string& bar() {return s;}
void baz(std::string const& x){}
s=std::string("hello"); // 1
s=foo(); // 2
std::string s2=bar(); // 3
baz("hello"); // 4
s=X().s; // 5
В (1) временный объект std::string
, созданный из строкового литерала, является rvalue.
В (2) объект, возвращаемый из foo()
, является rvalue.
В (3), bar()
возвращает ссылку, поэтому нет rvalues.
В (4) временный объект std::string
, неявно созданный из строкового литерала, является rvalue.
В (5) временный объект x
является значением r, поэтому поэтому член s
.
Выражения, такие как x+3
, как правило, приводят к временному, что, таким образом, является rvalue. Однако, если перегрузка оператора была использована для изменения типа возврата на ссылку, то результатом будет lvalue.