Почему при инициализации константного указателя значением null нет предупреждения или ошибки?

Это может быть очень простой вопрос, но я пытался найти ответ в SO и не смог найти на него точного ответа.

Какой смысл инициализировать указатель const с помощью nullptr?

int *const pi = nullptr;

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

Я попытался скомпилировать это с помощью g++ -w.

Кроме того, можете ли вы привести какой-нибудь вариант использования, когда указатель const будет инициализирован nullptr для действительной цели в реальном коде?

Потому что nullptr не является недопустимым. Я предполагаю, что для числового значения, такого как «пи», это не имеет особого смысла, но это ваш выбор как разработчика. Это все еще действующий код. В качестве примера я мог бы представить, что указатель (const) намеренно имеет значение null (навсегда), потому что некоторые функции отключены в зависимости от создания экземпляра класса.

Cedric 17.12.2020 10:12
nullptr является допустимым значением для указателя, и нет причин, по которым его нельзя допускать. if (something == pi) все еще действует.
nos 17.12.2020 10:13

«Пи не может быть эффективно использовано где-либо в программе». Почему вы так думаете? I можно эффективно использовать везде, где можно использовать nulltpr. Это не неправильно, просто не очень полезно давать другое имя nullptr

463035818_is_not_a_number 17.12.2020 10:13

Почему кто-то хочет запретить это? Точно так же вы могли бы сказать, почему что-то вроде (true and false and true and false) должно быть запрещено, потому что оно все равно оценивается как false. Стандарты существуют не для проверки вашей семантики.

Aziuth 17.12.2020 10:19

Умный компилятор @Aziuth должен предупредить об этом, не так ли? что-то вроде предупреждения: выражение всегда оценивается как ложное.

Vencat 17.12.2020 10:29

@Cedric: «Недействительно» означает, что это не ошибка, это не значит, что не должно быть предупреждения.

einpoklum 17.12.2020 10:34

ничего в нормативных документах не требует диагностики в этой ситуации, так что "должен" нет. Во всяком случае, вы объявили законный неизменяемый нулевой объект (шаблон)

Swift - Friday Pie 17.12.2020 10:43

@einpoklum Действительно, предупреждение было бы возможно. Я не знаю настоящей причины, по которой не выдается предупреждение, но, поскольку это «предположительно разумный» способ использования языка, такое предупреждение на практике было бы очень раздражающим, имхо. В отличие от, например. if (unsigned_type_value < 0), и в этом случае можно было бы явно ожидать ошибки пользователя.

Cedric 17.12.2020 12:31

@Swift-FridayPie: обратите внимание, что вопрос был отредактирован, чтобы сосредоточиться не на стандарте языка, а на поведении компилятора. Должное раскрытие: я сделал это редактирование, полагая, что оно лучше отражает то, что хочет сказать ОП.

einpoklum 17.12.2020 14:42
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
9
717
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Не путайте const, что означает: «Значения не могут измениться после инициализации» с «это значение всегда, при любых обстоятельствах nullptr». Я имею в виду следующее:

 struct foo {
     int *const pi = nullptr;
     foo( int* p) : pi(p) {}
 };

Различные экземпляры foo могут иметь разные значения для pi.

Другой способ получить условие инициализации:

 int *const pi = (some_condition) ? nullptr : some_pointer;

Даже с равниной

 int *const pi = nullptr;

нет ничего плохого. По сути, это дает другое имя nullptr, поэтому вы можете использовать pi везде, где вы можете использовать nullptr. Например, как часовой в контейнере:

 int * const empty = nullptr;
 for ( int* element : container) {
     if (element != empty) do_something(element);
 }

Это можно считать запутыванием, и прямое использование nullptr было бы более читабельным. Хотя учтите, что значение дозорного изменяется позже, использование empty позволяет легко заменить все его вхождения.

Ответ принят как подходящий

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

#include<iostream>  

#define USENULL 1 // Comment out this line to get a 'valid' pointer!

int main()
{
    int a = 42;
    #ifdef USENULL
    int* const pi = nullptr;
    #else
    int* const pi = &a;
    #endif
    int* pa = pi;
    if (pa) std::cout << *pa;
    else std::cout << "null pointer";
    std::cout << std::endl;
    return 0;
}

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

Я думаю, что первый пример неуместен. Маскировка nullptr для экономии нескольких нажатий клавиш - плохая практика кодирования ИМХО. Рассмотрите возможность сохранения только второго, который более действителен.

einpoklum 17.12.2020 10:38

@einpoklum Согласен - мы не хотим поощрять плохие методы кодирования на SO! Отредактировано, чтобы удалить это.

Adrian Mole 17.12.2020 11:00

Я попытаюсь обобщить пример @AdrianMole (второй):

Иногда фрагмент кода является вырожденным случаем или исключительным случаем более общей инструкции или шаблона. Таким образом:

  • В примере @AdrianMole есть внешний параметр, который определяет, должно ли pi иметь значимое значение или нет. Вы не хотите слишком сильно искажать свой исходный код, чтобы приспособиться к этому сценарию, поэтому вы сохраняете тот же код, но используете nullptr, вероятно, с более поздней (во время выполнения) проверкой того, есть ли у вас nullptr или нет.

  • У вас может быть класс шаблона, где общее определение выглядит примерно так:

    template <typename T>
    class A  {
    int *const pi = whatever<T>();
    }
    

    со специализацией

    template <>
    class A<foo_t>  {
    int *const pi = nullptr;
    }
    

    потому что вы хотите избежать звонка whatever<foo>().

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

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

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