Почему оператор стрелки в C ++ не является псевдонимом *.?
18
В c ++ оператор * может быть перегружен, например, с помощью итератора, но оператор стрелки (->) (. *) Не работает с классами, которые перегружают оператор *. Я полагаю, что препроцессор может легко заменить все экземпляры -> на (* left) .right, и это сделает итераторы более привлекательными для реализации. Есть ли практическая причина отличаться -> или это просто особенность языка / дизайнеров?
Правило, foo->barравное (*foo).barтолько для встроенных операторов.
Унарный operator *не всегда имеет семантику разыменования указателя. Я мог бы создать библиотеку, в которой это означало бы перестановку матриц, совпадения парсера с нулем или более или почти все что угодно.
Это сделало бы язык более утомительным, если бы все, что перегружало унарный код operator *, внезапно получило бы operator ->то, о чем вы не просили, с семантикой, которая может не иметь смысла.
operator -> может быть перегружен отдельно, поэтому, если вы хотите его, вы можете перегрузить его с минимальными усилиями.
Также обратите внимание, что такая перегрузка будет обладать некоторыми довольно интересными свойствами, такими как автоматическая цепочка operator ->вызовов, пока один в цепочке не возвратит необработанный указатель. Это очень полезно для умных указателей и других типов прокси.
Что иллюстрирует ваш пример? Вы возвращаете умный указатель на строку и как-то выводите размер? Я не совсем понимаю.
Тревор Хикки
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некоторого класса if ++, + =, =, и + предоставляются.
Таким образом, кажется, что разработчики языка предоставили отдельные точки перегрузки, потому что вы могли бы хотеть перегрузить их по-другому, а не предполагать, что вы всегда хотите, чтобы они были одинаковыми.
Как правило, C ++ предназначен для обеспечения гибкости, поэтому перегрузки *и ->разделены. Хотя это довольно необычно, если вы хотите достаточно сильно, вы можете написать эти перегрузки, чтобы делать совершенно разные вещи (например, может иметь смысл для предметно-ориентированного языка, реализованного внутри C ++).
Тем не менее, итераторы сделать поддержку либо использование. В древних реализациях вы можете найти библиотеку, которая требует (*iter).whateverвместо iter->whatever, но если это так, то это ошибка в реализации, а не характеристика языка. Учитывая объем работы, связанной с реализацией всех стандартных контейнеров / алгоритмов / итераторов, неудивительно, что некоторые ранние выпуски были несколько неполными, но на самом деле они никогда не предназначались для этого.
->
цепочки операторов, пока не получит необработанный указатель на что-то, разыменовывать и получать доступ к члену этого оператора. Если оператор -> не цепочка, пример будет некорректным, так как shared_ptr не является необработанным указателем.A->B
цепочек синтаксиса не более 1 дополнительного вызова. То, что на самом деле делает двоичный синтаксис C ++ ->, не вызывает объектopeartor->
напрямую - вместо этого он смотрит на типA
и проверяет, является ли он необработанным указателем. Если он затем->
разыменовывает его и выполняетB
для него, в противном случае он вызывает объектoperator->
, разыменовывает результат (либо используя собственный необработанный указатель, либо другой,operator->
а затем выполняетB
результатx->m
следует интерпретировать как(x.operator->())->m
. Если LHSoperator->
снова имеет подходящую перегрузку , этот процесс повторяется до тех пор, пока не будет получен обычный(*x).m
эффект 5.2.5 / 2.«Язык программирования C ++» описывает тот факт, что эти операторы могут быть разными, но также говорит:
Таким образом, кажется, что разработчики языка предоставили отдельные точки перегрузки, потому что вы могли бы хотеть перегрузить их по-другому, а не предполагать, что вы всегда хотите, чтобы они были одинаковыми.
источник
Как правило, C ++ предназначен для обеспечения гибкости, поэтому перегрузки
*
и->
разделены. Хотя это довольно необычно, если вы хотите достаточно сильно, вы можете написать эти перегрузки, чтобы делать совершенно разные вещи (например, может иметь смысл для предметно-ориентированного языка, реализованного внутри C ++).Тем не менее, итераторы сделать поддержку либо использование. В древних реализациях вы можете найти библиотеку, которая требует
(*iter).whatever
вместоiter->whatever
, но если это так, то это ошибка в реализации, а не характеристика языка. Учитывая объем работы, связанной с реализацией всех стандартных контейнеров / алгоритмов / итераторов, неудивительно, что некоторые ранние выпуски были несколько неполными, но на самом деле они никогда не предназначались для этого.источник
(*i).m
является допустимым, поддерживалi->m
ту же семантику.