Почему C ++ не разрешает неконстантное константное преобразование в копии ctor? - PullRequest
2 голосов
/ 31 июля 2009

У меня есть два члена геттера:

Node* prev() { return prev_; }
int value()  { return value_ }

Обратите внимание на отсутствие идентификаторов const (я забыл их, но теперь я хочу знать, почему это не сработает). Я пытаюсь получить это для компиляции:

Node(Node const& other) : prev_(other.prev()), value_(other.value()) { }

Компилятор отклоняет это. Я думал, что C ++ позволяет неконстантному константному преобразованию в параметрах функции, таких как:

{
   Foo(int bar);
}

Foo(const int bar)
{
   //lala
}

Почему он не позволяет мне делать то же самое с конструктором копирования? Идентификатор const означает, что я обещаю ничего не менять, так почему это имеет значение, если я получу свое значение из постоянного или неконстантного источника?

Ответы [ 5 ]

14 голосов
/ 31 июля 2009

Вы не пытаетесь сделать неконстантное преобразование в константное. Вы пытаетесь вызвать два метода, которые не являются константной и константной ссылкой (вызовы prev и value). Этот тип операции строго запрещен семантикой const.

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

3 голосов
/ 31 июля 2009

Я думал, что C ++ позволяет неконстантному преобразованию const в параметрах функции, таких как:

Вы пытаетесь сделать прямо противоположное: от конста до неконста. Вызывая неконстантную функцию-член, компилятор связывает выражение (которое является Node const с Node& для привязки указателя this). Таким образом, он отбросит const - недопустимо, потому что вызовет неконстантную функцию для объекта const.

/* class Foo */ {
   Foo(int bar);
}

/* Foo:: */ Foo(const int bar)
{
   //lala
}

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

Если вам интересно: если две вышеупомянутые версии идентичны - почему не ниже? Тогда это происходит потому, что ниже содержится еще один уровень косвенности: ниже сама ссылка ( верхний уровень ) не является константой (вы не можете поместить const непосредственно в саму ссылку), но Тип, на который ссылаются, является const. В это время const не игнорируется при определении типа функции.

/* class Foo */ {
   Foo(int &bar);
}

// different thing, won't work!
/* Foo:: */ Foo(const int &bar)
{
   //lala
}
2 голосов
/ 31 июля 2009

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

Простое исправление для этого - просто пометить ваши функции получения как const:

Node* prev() const { return prev_; }
int value()  const { return value_; }

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

Тем не менее, у вас есть еще одна потенциальная проблема в том, что если ваш экземпляр ctor выглядит так:

Node(Node const& other) : prev_(other.prev()), value_(other.value()) { }

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

2 голосов
/ 31 июля 2009

В объявлении конструктора Node(Node const& other) аргумент other объявлен const. Это означает, что вы можете вызывать только const методы.

Вы вызываете other.prev() и other.value() из конструктора, оба из которых не являются const методами, поэтому компилятор жалуется.

2 голосов
/ 31 июля 2009

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

...