перегруженные методы класса const и non-const, возвращающие ссылки в переполнении стека

У меня есть класс структуры данных в C ++ с аксессором к некоторому объекту (может быть большим), и у меня есть const и неконстантные методы, использующие этот аксессор, поэтому мне нужно его перегрузить. Я ищу рецензия кода ниже — может быть, есть способ сделать то же самое, что чище?

Насколько я понимаю, есть два способа добиться этого без дублирования кода в методе доступа в следующем случае, метод get (). Я не уверен, есть ли серьезные проблемы с любым из этих двух методов и Я хотел бы получить руководство Вот.

Мне нравится метод А, потому что:

  • только один const_cast
  • const-версия метода get () возвращает копию
  • неконстантный метод напрямую получает неконстантную ссылку

Мне не нравится метод А, потому что:

  • неконстантный метод get () является константным только по контракту (не проверяется компилятором)
  • сложнее получить const-ссылку, хотя и не невозможно

Мне нравится метод B, потому что:

  • Константность метода const get () проверяется компилятором
  • копия возвращаемого объекта контролируется пользователем

Мне не нравится метод B, потому что:

  • требует двух const_casts, которые трудно читать

Вот (минимальный) пример кода двух случаев.

/**
* summary:
* Two classes with an overloaded method which is
* guaranteed (by contract) not to change any
* internal part of the class. However, there is a
* version of this method that will return a non-const
* reference to an internal object, allowing the user
* to modify it. Don't worry about why I would ever
* want to do this, though if you want a real-world
* example, think about std::vector<>::front()
*
* The difference between A and B can be summarized
* as follows. In both cases, the second method merely
* calls the first, wrapped with the needed
* const_cast's
*
* struct A {
*     int& get();
*     int  get() const;
* };
*
* struct B {
*     const int& get() const;
*           int& get();
* };
*
**/

struct A
{
int _val;

A() : _val(7) {};

// non-const reference returned here
// by a non-const method
int& get()
{
// maybe lots of calculations that you do not
// wish to be duplicated in the const version
// of this method...
return _val;
}

// const version of get() this time returning
// a copy of the object returned
int get() const
{
// CONST-CAST!!?? SURE.
return const_cast<A*>(this)->get();
}

// example of const method calling the
// overloaded get() method
int deep_get() const
{
// gets a copy and makes
// a copy when returned
// probably optimized away by compiler
return this->get();
}
};

struct B
{
int _val;

B() : _val(7) {};

// const reference returned here
// by a const method
const int& get() const
{
// maybe lots of calculations that you do not
// wish to be duplicated in the non-const
// version of this method...
return _val;
}

// non-const version of get() this time returning
// a copy of the object returned
int& get()
{
// CONST-CAST!? TWO OF THEM!!?? WHY NOT...
return const_cast<int&>(const_cast<const B*>(this)->get());
}

// example of const method calling the
// overloaded get() method
int deep_get() const
{
// gets reference and makes
// a copy when returned
return this->get();
}
};int main()
{
A a;
a.get() = 8;  // call non-const method
a.deep_get(); // indirectly call const method

B b;
b.get() = 8;  // call non-const method
b.deep_get(); // indirectly call const method
}

1

Решение

Константность функции-члена должна решаться на основе вопроса: используется функция-член в контексте для изменения объекта? (либо член изменяет объект напрямую, либо возвращает ссылку / указатель на внутренние данные, чтобы внешние абоненты могли изменить объект). Если да, сделайте его неконстантным, иначе сделайте его константным.

Компилятор будет правильно выбирать между перегрузками, которые отличаются только константностью. Тем не мение, возвращаемый тип не используется в разрешении перегрузки. Кроме того, возврат по стоимости / по ссылке должен решаться только на основе ожидаемой стоимости и предполагаемого права собственности на то, что вы собираетесь вернуть. К счастью, C ++ 11 облегчает жизнь, предоставляя переместить семантику. Это означает, что вы можете успешно возвращать большие структуры данных по значению. Вернуть по ссылке только в том случае, если указанный объект переживет внешний вызывающий объект.

Мне кажется, что ваш int& get() должен быть переименован void set(int) и что вы могли бы разделить int get() const в помощник вычислений и собственно get()

class C
{
int val_;
public:
void modify()   { /* some expensive computation on val_ */ }
int get() const { return val_; }
void set(int v) { val_ = v_; }
};

В качестве альтернативы, если вы хотите сохранить get() функции, вы могли бы сделать

class D
{
int val_;
public:
void modify()    { /* some expensive computation on val_ */ }
int get() const  { modify(); return val_; }
int& get()       { modify(); return val_; } // no const-cast needed
};
2

Другие решения

Других решений пока нет …