C ++ decltype і дужки - чому?


32

Тема обговорювалася раніше , але це не дублікат.

Коли хтось запитує про різницю між decltype(a)і decltype((a)), звичайна відповідь - aце змінна, (a)є виразом. Я вважаю цю відповідь незадовільною.

По-перше, aце також вираз. Параметри первинного вираження включають, серед іншого, -

  • (вираз)
  • id-вираз

Що ще важливіше, фразування для decltype розглядає круглі дужки дуже, дуже явно :

For an expression e, the type denoted by decltype(e) is defined as follows:
(1.1)  if e is an unparenthesized id-expression naming a structured binding, ...
(1.2)  otherwise, if e is an unparenthesized id-expression naming a non-type template-parameter, ...
(1.3)  otherwise, if e is an unparenthesized id-expression or an unparenthesized class member access, ...
(1.4)  otherwise, ...

Тож питання залишається. Чому дужки ставляться по-різному? Хтось знайомий з технічними документами чи дискусіями в комітетах? Чіткий розгляд дужок призводить до думки, що це не нагляд, тому я повинен мати технічну причину, яку я пропускаю.


2
"звичайна відповідь - а - це змінна, (а) - це вираз" Що вони означають, (a)- це вираз, і aце вираз і змінна ".
HolyBlackCat

Відповіді:


18

Це не недогляд. Цікаво, що в Decltype і auto (версія 4) (N1705 = 04-0145) є твердження:

У правилах decltype зараз прямо зазначено, що decltype((e)) == decltype(e)(як це запропонував EWG).

Але в Decltype (редакція 6): запропоноване формулювання (N2115 = 06-018) однією із змін є

Виразно виражене значення в decltype не вважається an id-expression.

У формулюванні немає обгрунтування, але я припускаю, що це своєрідне розширення decltype, використовуючи дещо інший синтаксис, іншими словами, воно мало на меті диференціювати ці випадки.

Використання для цього показано на чернетках C ++9.2.8.4:

const int&& foo();
int i;
struct A { double x; };
const A* a = new A();
decltype(foo()) x1 = 17;        // type is const int&&
decltype(i) x2;                 // type is int
decltype(a->x) x3;              // type is double
decltype((a->x)) x4 = x3;       // type is const double&

Що насправді цікаво, це те, як це працює з returnтвердженням:

decltype(auto) f()
{
    int i{ 0 };
    return (i);
}

Моя Visual Studio 2019 пропонує мені видалити надлишкові дужки, але насправді вони перетворюються на те, decltype((i))що змінює повернене значення, int&яке робить його UB з моменту повернення посилання на локальну змінну.


Дякуємо, що вказали на походження! Мало того, що дельтифікація не могла бути вказана по-іншому, виявляється, це було спочатку. Документ rev-6 прямо додає цю кумедну поведінку в дужках, але пропускає обґрунтування :(. Я думаю, це настільки ж близький до відповіді, як ми отримаємо зараз ..
Офек Шилон,

І все ж ніхто не запропонував вирішити нерозумну проблему, зробивши її двома чіткими ключовими словами, для двох різних функцій? Одна з найбільших помилок комітету (яка історично зробила багато несподіваних помилок).
цікавогут

13

Чому дужки ставляться по-різному?

Парези не трактуються по-різному. Це незрозумілий ідентичний вираз, який трактується по-різному.

Коли в дужках є, тоді застосовуються регулярні правила для всіх виразів. Тип і ціннісна категорія видобуваються та кодифікуються за типом decltype.

Спеціальне положення є для того, щоб ми могли легше написати корисний код. Застосовуючи decltypeдо імені змінної (члена), ми зазвичай не хочемо, щоб якийсь тип, який представляє властивості змінної, розглядався як вираз. Натомість ми хочемо просто вказати тип, за допомогою якого оголошена змінна, без необхідності застосовувати тонну ознак типу, щоб отримати її. І саме decltypeце визначено для того, щоб дати нам.

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


Так для intелемента iз a, decltype(a.i)це в intтой час як decltype((a.i))є int&(передбачається , що aНЕ const)? Оскільки вираз a.iприсвоюється?
n314159

1
@ n314159 - В цьому суть. Вираз a.iє не-const lvalue, тому ви отримуєте non-const lvalue посилання для (a.i).
StoryTeller - Невідповідна Моніка

Чому "регулярні правила для всіх виразів" вказують, що їх тип є посиланням? Чому "властивості змінної як виразу" включають властивість бути посиланням? Це якийсь природний дефолт?
Офек Шилон

1
@OfekShilon - їх тип не є посиланням. Тип виразу ніколи не аналізується як еталонний . Але derectpe може вирішувати лише тип, і це покликане сказати нам не тільки тип виразу, а й його ціннісну категорію. Категорія значень кодифікована за референсними типами. lvalues ​​є &, xvalues ​​є &&, а prvalues ​​не є еталонними типами.
StoryTeller - Невідповідна Моніка

@StoryTeller точно, немає "природної" різниці між типами виразів і невиразів. Що робить розмежування штучним.
Офек Шилон

1

Попередньо C ++ 11 потрібні інструменти (мови) для отримання двох різних видів інформації :

  • тип виразу
  • тип змінної, як вона була оголошена

Через характер цієї інформації функції потрібно було додати в мові (це неможливо зробити в бібліотеці). Це означає, що нові ключові слова. Стандарт міг би ввести для цього два нові ключові слова. Наприклад, exprtypeщоб отримати тип виразу та decltypeотримати тип оголошення змінної. Це був би ясний, щасливий варіант.

Однак стандартний комітет завжди намагався уникати введення нових ключових слів у мову, щоб мінімізувати злом старого коду. Зворотна сумісність - це основна філософія мови.

Так з C ++ 11 , ми отримали тільки одне ключове слово , яке використовується для двох різних речей: decltype. Те, як вона розмежовує два способи використання, полягає в тому, що по- decltype(id-expression)різному трактується . Це було свідоме рішення комітету, (невеликий) компроміс.


Я пам’ятаю, як чула це на розмові на cpp Однак я не сподіваюся знайти джерело. Було б чудово, якби хтось його знайшов.
болов

1
C ++ історично додав купу нових ключових слів, багато без підкреслення, а деякі - з одним англійським словом. Раціональне дійсно абсурдне.
цікавогут

@curiousguy кожне введене ключове слово зіткнеться з існуючими символами користувача, тому комітет додає великої ваги рішенню додати нові зарезервовані ключові слова до мови. Це не абсурдне іммо.
болов

Неправда: exportбуло введено. Якщо у вас є export(раніше всі шаблони за замовчуванням були "експортовані"), ви можете мати такі речі, як decltypeі constexpr. Очевидно, що додати registerіншою мовою було б проблематично.
допитливий хлопець

@bolov Дякую! (1) Це міркування чи знання? Чи знаєте ви дискусії, де мотивацією було уникати зайвого ключового слова? (2) Чи можете ви навести приклад, коли ідентичний вираз насправді потрібно трактувати по-різному, якщо він використовується "як вираз"? (Що це навіть означає?)
Офек Шилон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.