2013-12-04 5 views
0

Если у нас есть такое определение классаКак написать метод получения так, что он возвращает RValue

class BusDetails 
{ 
private: 
    string m_busName; 
public: 
    string BusName() const { return m_busName; } 
}; 

как бы метод геттер быть изменен или используется, так что, используя возвращаемое значение как Lvalue бы дать ошибка компилятора?

Например, если я использую его как

int main(void) 
{ 
    BusDetails bus; 

    bus.BusName() = "abc"; // <--- This should give a compiler error 
    cout << bus.BusName() << endl; 

    return 0; 
} 

я не получаю ошибку компилятора, поэтому, очевидно, назначение работ, но результат, как ожидалось.

Update: такое поведение, как ожидается, со встроенным типов (то есть компилятор выдает ошибку в приведенной выше строке кода, если у меня есть int как тип возвращаемого вместо string).

+1

Обратите внимание, что он возвращает значение rvalue, за исключением того, что 'operator =' on 'srd :: string' работает с rvalue слева, вы все равно можете назначить ему! – Yakk

+0

@Yakk, не могли бы вы расширить свой комментарий в ответ, поскольку я думаю, что он действительно отвечает на мой вопрос и помогает объяснить какое-то интересное поведение. – mihai

ответ

3

BusName() был объявлен как функция const. Поэтому он не может изменять членов. Ваша функция должна возвращать string& и не быть константой.

string& BusName() { return m_busName; } 

Кроме того, вы можете добавить на константный объект (this является Const):

const string& BusName() const { return m_busName; } 
+0

Да, я не хочу менять участника, я только хочу его прочитать. Вторая линия делает трюк. Благодарю. – mihai

+0

@Mihai Если вы не хотите изменять значение, почему ваш пример говорит 'bus.BusName() =" abc ";'?! – luk32

+0

Потому что я не хочу, чтобы это выражение НЕ приводило к ошибкам компилятора. На первый взгляд кажется, что это делает интуитивную вещь, но это не так. – mihai

1

Ну это своего рода ожидаемое поведение, что вы написали.

Вы возвращаете копию m_busName. Потому что вы не возвращаете ссылку. Поэтому создается временная копия возвращаемой переменной, а затем выполняется присвоение. operator= - это "abc".

Таким образом, путь будет string& BusName() const { return m_busName; }. Но это должно привести к ошибке компилятора.

Вы как будто хотите противоречивые вещи. Вы говорите string BusName() const, но вы хотите вернуть ссылку, которая позволит изменить состояние объекта.

Однако, если вы не зарекайся объект не изменится вы можете отказаться от const и идти с

string& BusName() { return m_busName; }; 

Или, если вы хотите сохранить const

const string& BusName() const { return m_busName; }; 

однако это должно дать ошибка в задании, естественно.

То же самое касается функций. Если вы передаете аргумент по ссылке, это ссылка. Если вы видите, что вы изменяете копию, вы, должно быть, не передали ее по ссылке, а по значению.

+0

Я просто не видел смысла в возврате lvalue, который вы не можете использовать, а именно bus.BusName(). Как и в настоящее время, вы можете использовать его как lvalue для присвоения ему чего-либо, но впоследствии вы не сможете его использовать. Или, может быть, только что-то вроде «string busName = bus.BusName() =« abc »», что не имеет для меня никакого смысла. В любом случае значения const string & return делают трюк. – mihai

+1

Да, но так оно и есть, согласуется, по крайней мере, для меня. Было бы странно, по моему вкусу (опять же), если вызов функции будет неявно изменен для возврата ссылки. Также вы можете использовать возвращаемое значение 'string & busName = bus.BusName(); busName = "abc"; cout << busName'. Таким образом, вы работаете над копией. – luk32

2

Непонятно, какое поведение вы хотите.

  • Если вы хотите присваивание быть ошибкой, и сохранить все гибкости возвращаемого значения (например, вы можете изменить код для возврата вычисленного значения), вы можете вернуть std::string const. (Это будет препятствовать перемещению семантики, но это вообще не большая проблема.)

  • Если вы хотите, чтобы иметь возможность изменить «элемент», но все еще хочет сохранить гибкость в отношении к тому, как это реализовано в классе , то вам следует предоставить метод сеттера. Одно соглашение (не единственное) состоит в том, чтобы предоставить функцию-геттер , как у вас есть (но возвращает std::string const), и предоставляют функцию с тем же именем void BusName( std::string const& newValue ), чтобы установить значение. (Другие конвенции будут использовать имя как SetBusName или вернуть старое значение, поэтому клиентский код может сохранить и восстановить его, или возврата *this, поэтому клиентский код может приковать операции:. obj.BusName( "toto" ).SomethingElse( /*...*/ )

  • Вы можете также обеспечивает неконстантный элемент, возвращающий ссылку на неконстантный. Если вы сделаете это, тем не менее, вы можете также сделать элемент данных общественности.

  • Наконец, вы можете предоставить неконстантный член который возвращает какой-то прокси-клас s, так что присвоение ему на самом деле вызовет функцию сеттера, и преобразование ее в std::string будет вызовите геттер. Это, безусловно, самый гибкий, , если вы нуждаетесь в поддержке модификаций клиента, но также далеко не самый сложный, поэтому вы, возможно, не захотите его использовать, если вам не нужно .

1

Функция возвращает rvalue.

Проблема в том, что std::string::operator=работает с rvalue слева. До C++ 11 было трудно или невозможно предотвратить его работу: в C++ 11 они добавили (относительно поздно) то, что в разговорной речи известно как ссылки rvalue на this: возможность перегружать методы и операторы класса в зависимости от состояние rvalue объекта.

Однако, std::string не был изменен, возможно, для смешения не так много времени и неприязни к нарушению существующего кода без уважительной причины.

Вы можете исправить эту проблему несколькими способами. Вы можете написать свой собственный класс строк, который подчиняется ссылке rvalue на this. Вы можете спуститься с std::string и заблокировать operator=. Вы могли бы написать объект-аксессор, который имеет -std::string, который может вводить-std::string&& a d std::string& (на основе состояния rvalue this) неявно, но назначает блоки с использованием метода delete d.

У всех трех проблем. Последнее имеет наименьшее количество проблем, второе - самые скрытые ловушки, первое - просто тяжелая работа.

Смежные вопросы