Какой должен быть константный квалификатор у метода, если последний возвращает не ссылку, а копию?

Если у структуры есть функция, которая возвращает ссылку на данные

struct foo{
  public
    using value_type = std::pair<int, int>;

  public:
    const value_type& data() const {
      return _data;
    }

    value_type& data() {
      return _data;
    }

  private:
    value_type _data = {1, 2};
};

то тогда:

  • константная функция возвращает тип const value_type&
  • НЕконстантная функция возвращает тип value_type&

И понятно, что это делается для того, чтобы мы могли менять неконстантный объект и не могли - константный.

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

struct foo{
  public
    using value_type = std::pair<int, int>;

  public:
    /*const*/ value_type data() const {
      return {0, 1};
    }

    /*const*/ value_type data() {
      return {0, 1};
    }
};
  1. По идее, чтобы первый и второй код были похожи, нужно
  • у константного метода возвращать константный объект
  • у НЕконстантного метода возвращать НЕконстантный объект
  1. С другой стороны, мы возвращаем объект, изменив который, в экземпляре класса foo ничего не поменяется, значит, чтобы не вводить никого в заблуждение нужно:
  • у константного метода возвращать константный объект
  • у НЕконстантного метода возвращать константный объект, тогда никто не запутается, потому что объект вообще нельзя будет менять.
  1. В clang-tidy есть предупреждение, которое говорит, что возвращать константные объекты не нужно вообще никогда, а это значит, что нужно:
  • у константного метода возвращать НЕконстантный объект
  • у НЕконстантного метода возвращать НЕконстантный объект

Как же быть?


Ответы (2 шт):

Автор решения: AR Hovsepyan
  • Если константная функция возвращает ссылку на член(по которой можно изменить состояние объекта), то эта ссылка должна быть константной, потому что состояние константного объекта не разрешено изменять(константность объекта не разрешит), а не для того, чтобы мы не могли изменить(мы и без этого не можем).

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

  • Константность возвращаемого объекта предназначена для вызывающей функции, и тут не имеет значения константность метода, а имеет значение ваши намерения.

Честно говоря, не смог представить ситуацию, когда нужно из метода возвращать константную ссылку на член.

Возвращение из константного или не константного метода константный объект, имеет много примеров.

→ Ссылка
Автор решения: HolyBlackCat

Достаточно одной константной функции: value_type data() const {return ...;}.

При возврате копии поменять оригинальное поле не получится - поэтому нет смысла городить отдельную неконстантную функцию.

Clang-tidy прав - при возврате по значению, возвращаемый тип никогда не должен быть const, потому что это не дает переместить получающийся временный объект, и заставляет вместо этого копировать его.


Если ваше возвращаемое значение - это копия члена класса, то при большом желании можно написать так:

value_type data() const & {return x;}
value_type data() && {return std::move(x);}

Чтобы при вызове функции на временном объекте, поле перемещалось, а не копировалось.

→ Ссылка