Подтвердить что ты не робот

Должен ли я возвращать объекты const в С++ 11?

Возможный дубликат:
Должен ли я возвращать объекты const?(Первоначальное название этого вопроса: int foo() или const int foo()?, объясняя, почему я его пропустил.)


Эффективный С++, пункт 3: Используйте константу, когда это возможно. В частности, рекомендуется возвращать объекты-константы, чтобы избежать непреднамеренного присваивания, например if (a*b = c) {. Я нахожу его немного параноидальным, тем не менее я слежу за этим советом.

Мне кажется, что возвращающие объекты const могут ухудшить производительность в С++ 11.

#include <iostream>
using namespace std;

class C {
public:
    C() : v(nullptr) { }

    C& operator=(const C& other) {
        cout << "copy" << endl;
        // copy contents of v[]
        return *this;
    }

    C& operator=(C&& other) {
        cout << "move" << endl;
        v = other.v, other.v = nullptr;
        return *this;
    }

private:
    int* v;
};

const C const_is_returned() { return C(); }

C nonconst_is_returned() { return C(); }

int main(int argc, char* argv[]) {
    C c;
    c = const_is_returned();
    c = nonconst_is_returned();
    return 0;
}

Отпечатки:

copy
move

Правильно ли выполняю назначение переноса? Или я просто не должен возвращать объекты const больше в С++ 11?

4b9b3361

Ответ 1

Возвращающиеся объекты const являются обходным путем, что вызывает множество других проблем. С++ 11 обеспечивает лучшее решение проблемы с присваиванием: Reference Qualifiers для функций-членов. Я пытаюсь объяснить это с помощью некоторого кода:

int foo(); // function declaration
foo() = 42; // ERROR

Выражение присваивания недопустимо, и, как вы можете видеть, не задействованы никакие объекты const. Здесь ничего особенного не происходит. Подумайте о встроенном операторе присваивания следующим образом. Понятно, что указанное выражение присваивания недопустимо, тогда как назначение с именованной переменной работает.

int& builtin_assign_int(int& lhs, const int& rhs);

Таким образом, можно ограничить параметры ссылками lvalue. Однако невозможно ограничить неявный первый параметр функций-членов (*this) на lvalue-ссылки.

Это изменилось с помощью С++ 11: Подобно конструкторам-константам для функций-членов, теперь есть ссылочные квалификаторы для функций-членов. Следующий код показывает использование операторов копирования и перемещения (обратите внимание на & после списка параметров):

struct Int
{
    Int(const Int& rhs) = default;
    Int(Int&& rhs) noexcept = default;
    ~Int() noexcept = default;
    auto operator=(const Int& rhs) & -> Int& = default;
    auto operator=(Int&& rhs) & noexcept -> Int& = default;
};

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

Int bar();
Int baz();
bar() = baz(); // ERROR: no viable overloaded '='

Поэтому нет необходимости возвращать объекты const. Вы можете ограничить операторов присваивания ссылками lvalue, чтобы все остальное работало, как ожидалось, в частности операции перемещения.

См. также:

Ответ 2

Возвращение объекта const по значению, возможно, никогда не было хорошей идеей, даже до С++ 11. Единственный эффект, который он имеет, заключается в том, что он не позволяет вызывающему абоненту вызывать неконстантные функции на возвращаемом объекте – но это не очень важно, учитывая, что вызывающий абонент получил копию объекта в любом случае.

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

Вы также правы, что в С++ 11 проблема еще более серьезная, так как возвращение const эффективно предотвращает операции перемещения. (Однако это не предотвращает копирование/перемещение.)

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

Ответ 3

Причина, по которой ваш вызов const_is_returned вызывает copy constructor, а не move constructor, заключается в том, что move должен изменять объект, поэтому он не может быть использован для объектов const. Я склонен говорить, что использование const в любом случае не рекомендуется и должно быть подвергнуто оценке программиста, иначе вы получите то, что продемонстрировали. Хороший вопрос.