C ++ 20 вводить std::common_reference
. Яке його призначення? Чи може хтось навести приклад його використання?
C ++ 20 вводить std::common_reference
. Яке його призначення? Чи може хтось навести приклад його використання?
Відповіді:
common_reference
Я вийшов із моїх зусиль, щоб придумати концептуалізацію ітераторів STL, що вміщує проксі-ітераторів.
У STL у ітераторів є два асоційовані типи, що представляють особливий інтерес: reference
та value_type
. Перший - це тип повернення ітератора operator*
, а тип value_type
(не-const, нереференційний) елементів послідовності.
У загальних алгоритмах часто виникає потреба робити такі дії:
value_type tmp = *it;
... тому ми знаємо, що між цими двома типами має бути певний зв’язок. Для непроксі-ітераторів зв’язок простий: reference
завжди value_type
, необов'язково const та посилання кваліфіковано. Ранні спроби визначення InputIterator
поняття вимагали, щоб вираз *it
був конвертованим const value_type &
, а для найбільш цікавих ітераторів достатньо.
Я хотів, щоб ітератори в C ++ 20 були потужнішими за це. Наприклад, розглянемо потреби того, zip_iterator
що ітераціює дві послідовності в режимі блокування. При відпуску a zip_iterator
ви отримуєте тимчасовий pair
з двох reference
типів ітераторів . Отже, zip
'a vector<int>
і a vector<double>
мали б такі пов'язані типи:
zip
ітератор reference
: pair<int &, double &>
zip
ітератор value_type
:pair<int, double>
Як бачимо, ці два типи не пов'язані один з одним просто додаванням кваліфікації cv- та ref вищого рівня. І все ж дозволяти обом типам довільно відрізнятись - це неправильно. Очевидно, що тут є певні стосунки. Але що таке взаємозв'язок, і що загальні алгоритми, які працюють на ітераторах, можуть безпечно припускати щодо двох типів?
Відповідь на C ++ 20 є те , що для будь-якого дійсного типу ітератора, проксі чи ні, типи reference &&
і value_type &
розділяють загальні посилання . Іншими словами, для якогось ітератора it
існує певний тип, CR
який робить наступне добре сформованим:
void foo(CR) // CR is the common reference for iterator I
{}
void algo( I it, iter_value_t<I> val )
{
foo(val); // OK, lvalue to value_type convertible to CR
foo(*it); // OK, reference convertible to CR
}
CR
є загальним посиланням. Усі алгоритми можуть розраховувати на те, що цей тип існує, і можуть використовувати його std::common_reference
для обчислення.
Отже, саме таку роль common_reference
відіграє STL у C ++ 20. Як правило, якщо ви не пишете загальні алгоритми чи проксі-ітератори, ви можете їх ігнорувати. Він знаходиться під обкладинкою, яка гарантує, що ваші ітератори виконують свої договірні зобов’язання.
EDIT: ОП також попросила прикладу. Це трохи надумано, але уявіть, що це C ++ 20, і вам надається діапазон r
типу випадкового доступу, R
про який ви нічого не знаєте, і ви хочете до sort
діапазону.
Далі уявіть, що з якоїсь причини ви хочете використовувати мономорфну функцію порівняння, наприклад std::less<T>
. (Можливо, ви стерли діапазон, і вам також потрібно стерти функцію порівняння та передати її через virtual
? Знову розтягнення.) Що має T
бути std::less<T>
? Для цього ви б використали common_reference
, або помічник, iter_common_reference_t
який реалізований з точки зору цього.
using CR = std::iter_common_reference_t<std::ranges::iterator_t<R>>;
std::ranges::sort(r, std::less<CR>{});
Це гарантовано працює, навіть якщо діапазон r
має проксі-ітератори.
pair<T&,U&>
і pair<T,U>&
було б спільне посилання, і було б просто pair<T&,U&>
. Однак, для std::pair
цього не існує перетворення pair<T,U>&
на, pair<T&,U&>
навіть якщо таке перетворення є принциповим. (Це, до речі, тому ми не маємо zip
виду в C ++ 20.)
pair
замість типу, який може бути спеціально розроблений для його призначення з відповідними неявними конверсіями, якщо це потрібно?
std::pair
; будь-який відповідний тип, подібний до пари, з відповідними перетвореннями буде робити, а range-v3 визначає такий тип, що нагадує пару. Щодо Комітету, ЛЕВГ не сподобалась ідея додавати до Стандартної бібліотеки тип, який був майже, але не зовсім std::pair
, будь то нормативний чи ні, не попередньо старанно ставлячись до плюсів і мінусів просто зробити std::pair
роботу.
tuple
, pair
, tomato
, to
- MAH
- to
. pair
має цю приємну особливість, що ви можете отримати доступ до елементів за допомогою .first
та .second
. Структуровані прив’язки допомагають дещо незручно працювати з tuple
s, але не всіма.