Const до або const after?


145

Для початку ви, мабуть, знаєте, що constїх можна використовувати для того, щоб зробити дані об'єкта або покажчик не змінювати, або і те і інше.

const Object* obj; // can't change data
Object* const obj; // can't change pointer
const Object* const obj; // can't change data or pointer

Однак ви також можете використовувати синтаксис:

Object const *obj; // same as const Object* obj;

Єдине, що, мабуть, має значення - це в яку сторону зірочки ви поставите constключове слово. Особисто я вважаю за краще поставити constліворуч від типу, щоб вказати, що дані не підлягають зміні, оскільки я вважаю, що вони краще читаються в моєму мисленні зліва направо, але який синтаксис з’явився першим?

Що ще важливіше, чому існує два правильних способи вказівки constданих і в якій ситуації ви віддасте перевагу або потребуєте один над іншим, якщо такі є?

Редагувати:

Так звучить, що це було довільним рішенням, коли стандарт про те, як компілятори повинні інтерпретувати речі, був розроблений задовго до мого народження. Оскільки constзастосовується до того, що знаходиться ліворуч від ключового слова (за замовчуванням?), Я думаю, вони вважали, що немає ніякого шкоди в додаванні "ярликів", щоб застосовувати ключові слова та класифікатори іншими способами, щонайменше, до тих пір, поки декларація не зміниться на розбір * або & ...

Так було і в С, тоді я припускаю?


9
У макросах завжди додайте const після типу, наприклад, #define MAKE_CONST(T) T constзамість того, #define MAKE_CONST(T) const Tщоб MAKE_CONST(int *)правильно розгорнутись на, int * constа не const int *.
jotik

6
Я бачив ці два стилі, які називаються "східний конст" та "західний конст".
Том Андерсон

13
@TomAnderson, але насправді це повинно бути "const east" та "const west".
ВАТ

Відповіді:


96

чому є два правильних способи вказівки constданих і в якій ситуації ви віддасте перевагу або потребуєте один над іншим, якщо такі є?

По суті, причина того, що позиція constвсередині специфікаторів перед зірочкою не має значення, полягає в тому, що граматика C була визначена таким чином Керніган і Річі.

Причина, по якій вони визначали граматику таким чином, ймовірно, що їх компілятор C аналізував введення зліва направо і закінчував обробку кожного маркера, коли він споживав це. Споживання *токена змінює стан поточної декларації на тип вказівника. Зустріч constпісля *засобів constкласифікатор застосовується до декларації вказівника; зустрічаючи його перед *засобом, на який класифікатор застосовується до даних, на які вказують.

Оскільки смислове значення не змінюється, якщо constкласифікатор з'являється до або після специфікаторів типу, він приймається будь-яким способом.

Аналогічний випадок виникає при оголошенні покажчиків функцій, де:

  • void * function1(void)оголошує функцію, яка повертається void *,

  • void (* function2)(void)оголошує покажчик функції на функцію, яка повертається void.

Знову ж таки, що слід помітити, що синтаксис мови підтримує синтаксичний синтаксичний аналіз.


7
Керніган є співавтором книги, але не брала участь у дизайні С, а лише Річі.
Том Зіч

8
Я ніколи не міг згадати, хто з них є. Завдяки вашому поясненню я нарешті маю мнемотехніку запам'ятати це. Дякую! Перед тим, як *аналізатор компілятора не знає, що це покажчик, тому це значення для значення даних. Після * це пов'язано з постійним покажчиком. Блискуча. І нарешті це пояснює, чому я можу const charтак добреchar const .
Віт Бернатик

2
Здогадка, чому це було зроблено таким чином, здається мені досить слабкою / суперечливою. Тобто, якби я визначав мову і писав компілятор, і я хотів би зробити його простим і "проаналізувати введення зліва направо і закінчити обробляти кожен маркер так, як він його споживає", як ви кажете, мені здається Я б вимагав, constщоб завжди прийшов після того, як це було кваліфіковано ... саме так, я завжди міг закінчити обробку const одразу після того, як буду споживати його. Тож це, мабуть, є аргументом для заборони заходу-констату, а не для його дозволу.
Дон Хетч

3
"Оскільки смислове значення не змінюється, якщо класифікатор const з'являється до або після специфікаторів типу, він приймається будь-яким способом." Чи не це кругові міркування? Питання в тому, чому смислове значення визначається так, тому я не думаю, що це речення нічого не сприяє.
Дон Хетч

1
@donhatch Ви повинні пам’ятати, що стосовно сьогоднішнього дня та припущень, які ми робимо, ґрунтуючись на нашому знайомстві з хорошим дизайном мови програмування, мови тоді були досить новими. Також, чи є у людини дозвільна чи обмежена мова, це судження про цінність. Наприклад, чи повинен пітон мати ++оператора? "Речення", імхо, допомогло мені зрозуміти, що немає жодної конкретної причини, крім того, що вони могли. Можливо, вони зробили б інший вибір сьогодні, а може ні.
ragerdl

75

Правило таке:

const застосовується до речі, що залишилася від неї. Якщо зліва немає нічого, то це стосується речі справа від цього.

Я вважаю за краще використовувати const праворуч від речі, щоб бути const лише тому, що це "оригінальний" спосіб визначення const.

Але я думаю, що це дуже суб’єктивна точка зору.


18
Я вважаю за краще ставити його зліва, але думаю, що ставити її праворуч має більше сенсу. Ви, як правило, читаєте типи в C ++ справа наліво, наприклад Object const *, це вказівник на об'єкт const. Якщо ви поставите constліворуч, він буде читатися як вказівник на об'єкт const, який насправді не надто добре протікає.
Collin Dauphinee

1
Мені здається, що ліворуч - це відповідність стилю людини іншим видам декларацій C (комп'ютерно - це не правильно, оскільки constце не клас зберігання, але люди не парсери).
geekosaur

1
@Heath Я вважаю, що це скоріше керівництво, ніж правило, і я часто чув це як спосіб запам'ятати, як компілятор буде його інтерпретувати ... Я розумію, як це працює, тому мені було цікаво лише думка про процес рішення підтримати його обома способами.
AJG85

3
@HeathHunnicutt правило існує, але воно трохи складніше: c-faq.com/decl/spiral.anderson.html
imallett

2
@HeathHunnicutt: правило спіралі - це розширена версія коментаря першого коментатора "Ви, як правило, читаєте типи в [C /] C ++ справа наліво". Я припускав, що ви суперечите цьому. Однак я думаю, що натомість ви посилалися на саму відповідь.
imallett

57

Я віддаю перевагу другому синтаксису. Це допомагає мені відслідковувати "що" є постійним, читаючи декларацію типу справа наліво:

Object * const obj;        // read right-to-left:  const pointer to Object
Object const * obj;        // read right-to-left:  pointer to const Object
Object const * const obj;  // read right-to-left:  const pointer to const Object

3
Саме так. "Постійний вказівник на постійний об'єкт" є Object const* const, ні const const Object*. "const" не може бути зліва, за винятком особливого випадку, коли так багато людей абсолютно люблять це. (Див. Хіт вище.)
cdunn2001

38

Порядок ключових слів у декларації не все так фіксовано. Є багато альтернатив "єдиному справжньому порядку". Подобається це

int long const long unsigned volatile i = 0;

або має бути

volatile unsigned long long int const i = 0;

??


25
+1 для абсолютно заплутаного визначення простої змінної. :)
Xeo

@rubenvb - Так, на жаль, вони є. Граматика просто говорить, що a decl-specifier-seq- це послідовність decl-specifiers. Граматика не дає наказу, а кількість завдань для кожного ключового слова обмежена лише деякими семантичними правилами (у вас може бути одне, constале два long:-)
Бо Перссон,

3
@rubenvb - Так, unsignedце тип, такий самий, як unsigned intі int unsigned. unsigned longінший тип, такий же, як unsigned long intі int long unsigned. Бачите візерунок?
Бо Персон

2
@Bo: Я бачу безлад, треба мати три, щоб побачити візерунок ;). Добре, дякую
rubenvb

1
Ви раніше мали змогу додати staticдо переплетення слів, але лише нещодавно укладачі поскаржилися на те, що staticпотрібно зайняти перше місце.
Марк Лаката

8

Перше правило - використовувати той формат, який вимагають ваші місцеві стандарти кодування. Після цього: введення constспереду не призводить до замішання під час залучення typedefs, наприклад:

typedef int* IntPtr;
const IntPtr p1;   // same as int* const p1;

Якщо ваш стандарт кодування дозволяє покажчикам typedef, тоді він дійсно повинен наполягати на введенні const після типу. У кожному випадку, але застосовуючи тип, const повинен слідувати тому, що він стосується, тому узгодженість також висловлюється на користь const після. Але місцеві вказівки щодо кодування все це перетворюють; різниця зазвичай не є достатньо важливою для повернення та зміни всього існуючого коду.


Я думаю , що може виділити причину , чому ми НЕ маємо визначень типів покажчиків в наших досить вільно певних стандартах в цьому магазині.
AJG85

1
Моя політика (коли я приймаю рішення самостійно) полягає в тому, щоб ставити const після (заради узгодженості) і не використовувати typedefs для покажчиків (або в цілому typedefs) :-). І BTW, string :: iterator vs. string :: const_iterator, мабуть, слід враховувати і ваше рішення. (Просто, щоб заплутати речі :-). Правильної відповіді немає.)
Джеймс Канзе

Ага так, я міг би включити поведінку const std::string::const_iteratorтакож на добру міру;)
AJG85

2
@JamesKanze - Почекай, допоможи мені тут ... Я не бачу плутанини у розміщеному прикладі. Що ще може const IntPtr p1означати, крім "постійного цілого вказівника" (тобто "постійного вказівника на ціле число")? Ніхто з розуму, навіть не знаючи, як IntPtrце визначено, не подумає, що p1це змінне. І з цього приводу, чому хтось невірно вважає, що *p1це незмінне? Більше того, розміщення const в іншому місці (наприклад, IntPtr const p1) взагалі не змінює семантику.
Тодд Леман

3
@ToddLehman Можливо, ви не бачите плутанини, але більшість програмістів на C ++ роблять це і систематично помиляються (без сумніву, допомагають такі речі, як std::vector<T>::const_iteratorце не ітератор, який є const, а те, на що він вказує).
Джеймс Канзе

7

Існують історичні причини, які є лівими або правими прийнятними. Stroustrup додав const до C ++ до 1983 року , але це зробило це до C до C89 / C90.

У C ++ є вагомий привід завжди використовувати const праворуч. Ви будете послідовні скрізь, оскільки функції члена const повинні бути оголошені таким чином:

int getInt() const;

1
... ну, "вагома причина" не настільки переконлива, тому що інші можливі місця для "const" не означали б те саме. const int& getInt(); int& const getInt();
Маестро

1
@Maestro: Я пропоную int const& getInt();краще, ніж еквівалент, const int& getInt();тоді як int& const getInt();ви порівнюєте його із надмірним (посилання вже є const), хоч і легальні, і зазвичай дають попередження. У всякому разі, сопзЬ на функції - члена змінює thisпокажчик в функції від Foo* constдо Foo const* const.
Нік Вестгейт

constфункція-член зовсім не означає одне і те ж - або це void set(int)&;якесь посилання на функцію?
Девіс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.