Должен ли я по-прежнему возвращать const объекты в C++11?

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


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

Мне кажется, что возвращение const-объектов может снизить производительность в C++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 объекты в C++11?

3 ответа

Решение

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

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

Назначение во второй строке приводит к ошибке времени компиляции для встроенного типа int в обоих C и C++. То же самое для других встроенных типов. Это связано с тем, что оператор присваивания для встроенных типов требует неконстантной lvalue-ссылки на левой стороне. Чтобы поместить его в код, оператор присваивания может выглядеть следующим образом (неверный код):

int& operator=(int& lhs, const int& rhs);

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

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

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 '='

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

Смотрите также:

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

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

Вы также правы, что в C++ 11 проблема еще более серьезна, так как возвращение const эффективно предотвращает ход операций. (Тем не менее, это не мешает копированию / переносу.)

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

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

Другие вопросы по тегам