Чому оператор стрілки в C ++ не є лише псевдонімом *.?


18

У c ++ оператор * може бути перевантажений, наприклад, з ітератором, але оператор стрілки (->) (. *) Не працює з класами, які перевантажують оператора *. Я думаю, що препроцесор міг би легко замінити всі екземпляри -> на (* зліва) .right, і це зробило б ітератори приємнішими для реалізації. чи є практична причина для того, щоб -> відрізнятись, чи це лише особливість мови / дизайнерів?

Відповіді:


16

Правило, foo->barрівне, (*foo).barсправедливо лише для вбудованих операторів.

Унар operator *не завжди має семантику відміни покажчика. Я міг би створити бібліотеку, в якій це означає перенесення матриці, нульове або більше парсерних збігів, або майже все, що завгодно.

Це зробило б мову набридливішою, якби щось, що перевантажує одинарне operator *, раптом отримає те, про що operator ->ви не просили, із семантикою, яка може не мати сенсу.

operator -> може завантажуватися окремо, тому якщо ви хочете, ви можете перевантажити її мінімальними зусиллями.

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

#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <string>
#include <iostream>
#include <ostream>

struct Foo
{
    boost::shared_ptr<std::string> operator -> () const
    {
        return boost::make_shared<std::string>("trololo");
    }
};

int main()
{
    Foo foo;
    std::cerr << foo->size() << std::endl;
}

Що ілюструє ваш приклад? Ви повертаєте розумний покажчик на рядок і якось виводить розмір? Я збентежений.
Тревор Хікі

2
Він ілюструє останній абзац моєї відповіді, як користуватися ->ланцюжками операторів, поки не отримає необроблений вказівник на щось, перенаправлення та доступ до його члена. Якщо оператор -> не ланцюг, приклад буде неправильно сформований, оскільки shared_ptr не є необробленим покажчиком.
Ларс Віклунд

@LarsViklund: у вашій відповіді є проблема: ви сказали, що "оператор-> ... автоматично ланцюжок оператора-> дзвінки, поки один з ланцюжків не поверне необроблений покажчик". Це не правильно - використання A->Bсинтаксичних ланцюжків не більше 1 додаткового дзвінка. Що насправді робить бінарний синтаксис C ++ -> - це не викликати opeartor->безпосередньо об'єкт - замість цього він переглядає тип Aта перевіряє, чи є його необробленим покажчиком. Якщо він потім ->дереффікує його та виконує Bна цьому, інакше він викликає об'єкт operator->, знеструмлює результат (використовуючи нативний вказівник із сировиною чи інший, operator->а потім виконує Bрезультат
Guss

@Guss: Я не можу знайти жодної глави та вірша для вашої претензії, а також відтворити її у компіляторі. C ++ 11 13.5.6 / 1 вказує на те, що, якщо є відповідне перевантаження, x->mслід інтерпретувати як (x.operator->())->m. Якщо LHS - це щось, що має відповідне перевантаження operator->знову, цей процес повторюється, поки не буде досягнуто звичайного (*x).mефекту 5.2.5 / 2.
Ларс Віклунд

8

"Мова програмування на C ++" описує той факт, що ці оператори різні, тому що вони можуть бути, але також говорить:

Якщо ви надаєте більше ніж один з цих операторів, можливо, було б доцільно надати еквівалентність так само, як розумно забезпечити це ++xта x+=1мати такий же ефект, як і x=x+1для простої змінної xдеякого класу, якщо ++, + =, = і + надаються.

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


7

Як правило, C ++ призначений для сприяння гнучкості, тому перевантаження *і ->є окремими. Хоча це досить незвично, якщо ви хочете досить сильно, ви можете написати ці перевантаження, щоб робити зовсім інші речі (наприклад, це може мати сенс для доменної мови, реалізованої всередині C ++).

Тим НЕ менше, ітератори зробити підтримку або використання. У старовинних реалізаціях ви можете знайти бібліотеку, яка потребує (*iter).whateverзамість цього iter->whatever, але якщо так, то це помилка в реалізації, а не характеристика мови. Враховуючи обсяг роботи, що займається впровадженням усіх стандартних контейнерів / алгоритмів / ітераторів, не дивно, що деякі ранні випуски були дещо незавершеними, але насправді вони ніколи не були призначені таким чином.


Я не розумів, що реалізовані стандартні бібліотечні контейнери ->, або що вони можуть завантажуватися.
Якоб Вайсблат

3
C ++ 03 24.1 / 1 вимагає, щоб будь-який ітератор, де (*i).mдіє, повинен підтримувати i->mоднакову семантику.
Ларс Віклунд
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.