Новые экспериментальные операторы Си++

Original author: Raymond Chen
  • Translation
Так часто приходится писать такой код:
x = (y + 1) % 10;
x = (y + 1) * (z - 1);
x = (double)(f(y) + 1);


Так как у операторов + и — такой низкий приоритет, то приходится постоянно заключать их в скобки, а это приводит к глубокому вложенному коду, который сложно понять.
В Visual Studio 2015 RC добавлена пара экспериментальных операторов, названных операторы–головастики. Они позволяют добавлять и вычитать единицу без необходимости прибегать к скобкам.
x = -~y % 10;
x = -~y * ~-z;
x = (double)-~f(y);


Они так названы, так как напоминают головастиков, которые плывут к или от значения. Тильда это голова, а минус — хвост.
Синтакс Значение Мнемоника
-~y y + 1 Головастик плывет к значению и делает его больше
~-y y — 1 Головастик плывет от значения и делает его меньше


Чтобы включить экспериментальную поддержку операторов–головастиков, добавьте в начале C++ файла:
#define __ENABLE_EXPERIMENTAL_TADPOLE_OPERATORS

Вот простая программа, которая демонстрирует применение операторов–головастиков:

#define __ENABLE_EXPERIMENTAL_TADPOLE_OPERATORS 
#include <ios>
#include <iostream>
#include <istream>
 
int __cdecl main(int, char**)
{
   int n = 3;
   std::cout << "3 + 1 = " << -~n << std::endl;
   std::cout << "(3 - 1) * (3 + 1) " << ~-n * -~n << std::endl;
   return 0;
}

Помните, что эти операторы всё ещё экспериментальные. Они не являются официальной частью языка Си++, но вы можете поиграть с ними и оставить ваши замечания здесь.

От переводчика
Решил всё-таки добавить объяснение того, что происходит непосредственно в статью.
При представлении чисел в дополнительном коде выполняется соотношение между арифметическими и побитовыми операциями:
-x = ~x + 1;

Из чего логично получается:
-~x = ~(~x) + 1 = x + 1;
~-x = (~-x + 1) - 1 = -(-x) - 1 = x - 1;

Т. е. никаких новых операторов–головастиков в Си++ не вводят, это просто шутка от Реймонда Чена.
Другие неочевидные операции в Си++ можно посмотреть на этой странице.
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 58

    +59
    жесть, мало того что люди путаются в пост и пре инкрементах, теперь ещё и головастики…
    и да код стал ГОРАЗДО понятнее
      +35
      Ужас какой. Так бы и то было лучше: "~-y, ~+y". Но все равно, сам лично это в жизни не напишу.
        +1
        Там есть комменты, почему ~+ не сработает, посмотрите саму статью в блоге.
        +19
        Думаю, что если приживуться, то название у них будет одно — операторы-сперматозоиды
          +13
          Поклонники Оберона негодуют!
            +22
            Что-то поздно для первого апреля.
            ~-y и -~y — валидный С/С++ код, теперь эти выражения будут иметь другое значение?.. А, ок, я понял, значение то же самое.
              0
              А если y не int?
                0
                тогда ~ обломится.
              +9
              Я на всякий случай обращу внимание на два факта:
              1. Эти операторы работают во многих современных компиляторах Си++, а не только в Visual C++
              2. Внимательно читаем теги статьи
                +3
                На самом деле зря про дефайн написал. Всем ясно, что дополнительный синтаксический сахар (если бы он был) не включается дефайнами )
                +3
                А, ну да. Вместо непонятного и сложного (y+1) теперь простой и понятный -~y.
                  +13
                  Вообще-то хороший пост для первого апреля:)
                  Данные операторы работают в любом компиляторе С++, даже в самом древнем, и основаны на том что операторы смены знака и битовой инверсии, записанные друг за другом, дают именно такой эффект — увеличение или уменьшение числа на единицу (в зависимости от порядка следования).
                    +1
                    А самое главное, что компилятор понимает этот трюк и оптимизирует его:
                    0000000000000000 <f>:
                       0:	8d 47 01             	lea    0x1(%rdi),%eax
                    
                    0000000000000010 <g>:
                      10:	8d 47 ff             	lea    -0x1(%rdi),%eax
                    
                      +8
                      Они на самом деле не кроссплатформенные, так как работают только на числах в дополнительном коде.
                        +1
                        А реально приходилось работать на архитектурах, где это не так? Вопрос без подкола, просто интересно, ибо я о таких не знаю.
                          +1
                          Про машины с числами в обратном коде я знаю только про UNIVAC, но стандарт явно отказывается от указания способа представления числа со знаком, так что гарантировать их работу нельзя.
                      +3
                      Ждём Emoji в качестве операторов.
                        +6
                        уже сегодня в C++ можно писать
                        int с[:>={
                          0
                        };
                        

                        Справа от слова int стоит улыбающийся человечек в шапке с помпоном.
                        +23
                        От создателей оператора приближения к нулю (x --> 0), операторы-головастики. Уже сегодня в любой версии любого компилятора. Попробуйте и вы!
                          +28
                          (╯°益°)╯彡┻━┻
                          Throw Exception.
                            +26
                            ┬──┬ ノ(°—°ノ)
                            Catch
                              +13
                              Вообще, большой простор, не только для C. Вот для bash отличный touch: ( • )( • )ԅ(‾⌣‾ԅ)
                                +11
                                ゚ω゚ノ= /`m´)ノ ~┻━┻ //*´∇`*/ ['_']; o=(゚ー゚) =_=3; c=(゚Θ゚) =(゚ー゚)-(゚ー゚); (゚Д゚) =(゚Θ゚)= (o^_^o)/ (o^_^o);(゚Д゚)={゚Θ゚: '_' ,゚ω゚ノ: ((゚ω゚ノ==3) +'_') [゚Θ゚] ,゚ー゚ノ :(゚ω゚ノ+ '_')[o^_^o -(゚Θ゚)] ,゚Д゚ノ:((゚ー゚==3) +'_')[゚ー゚] }; (゚Д゚) [゚Θ゚] =((゚ω゚ノ==3) +'_') [c^_^o];(゚Д゚) ['c'] = ((゚Д゚)+'_') [ (゚ー゚)+(゚ー゚)-(゚Θ゚) ];(゚Д゚) ['o'] = ((゚Д゚)+'_') [゚Θ゚];(゚o゚)=(゚Д゚) ['c']+(゚Д゚) ['o']+(゚ω゚ノ +'_')[゚Θ゚]+ ((゚ω゚ノ==3) +'_') [゚ー゚] + ((゚Д゚) +'_') [(゚ー゚)+(゚ー゚)]+ ((゚ー゚==3) +'_') [゚Θ゚]+((゚ー゚==3) +'_') [(゚ー゚) — (゚Θ゚)]+(゚Д゚) ['c']+((゚Д゚)+'_') [(゚ー゚)+(゚ー゚)]+ (゚Д゚) ['o']+((゚ー゚==3) +'_') [゚Θ゚];(゚Д゚) ['_'] =(o^_^o) [゚o゚] [゚o゚];(゚ε゚)=((゚ー゚==3) +'_') [゚Θ゚]+ (゚Д゚) .゚Д゚ノ+((゚Д゚)+'_') [(゚ー゚) + (゚ー゚)]+((゚ー゚==3) +'_') [o^_^o -゚Θ゚]+((゚ー゚==3) +'_') [゚Θ゚]+ (゚ω゚ノ +'_') [゚Θ゚]; (゚ー゚)+=(゚Θ゚); (゚Д゚)[゚ε゚]='\\'; (゚Д゚).゚Θ゚ノ=(゚Д゚+ ゚ー゚)[o^_^o -(゚Θ゚)];(o゚ー゚o)=(゚ω゚ノ +'_')[c^_^o];(゚Д゚) [゚o゚]='\"';(゚Д゚) ['_'] ( (゚Д゚) ['_'] (゚ε゚+(゚Д゚)[゚o゚]+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ ((゚ー゚) + (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((o^_^o) — (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚ー゚)+ ((o^_^o) — (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚Θ゚)+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ ((゚ー゚) + (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ ((゚ー゚) + (o^_^o))+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚Θ゚)+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ ((o^_^o) — (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((o^_^o) — (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚ー゚)+ ((o^_^o) — (゚Θ゚))+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (゚Θ゚)+ (゚Д゚)[゚o゚]) (゚Θ゚)) ('_');

                                исполняемый JavaScript, например
                                  +1
                                  Это эпично.
                                    0
                                    Мой JS ругается на этот код, что я делаю не так?
                                    пруф
                                    image
                                      +3
                                      Хабр что-то меняет в тексте. Балуйтесь
                                        0
                                        Спасибо за столь замечательную ссылку!

                                        P.S. вы не знаете как оно блин работает вообще?! ни черта не могу понять где там какой-нибудь eval запрятан, или что-то вроде того
                                          0
                                          А, всё, разобрался.
                                  0
                                  В owl-lisp ノ°—°ノ используется как оператор реверса. Пруф: github.com/aoh/owl-lisp/blob/master/owl/list.scm
                                  +9
                                  (╯°益°)╯彡┻━┻
                                  Throw Exception.

                                  Мои глаза мне говорят что скорее это оператор DROP TABLE из мира SQL.
                                    0
                                    Изначально да, но он и здесь неплохо смотрится.
                                  +24
                                  Новые затейливые вопросы на собеседованиях!

                                  -~~-3+-~~--~~--~~-2
                                  


                                  Синтаксический порошок какой-то
                                    +5
                                    А вопрос — «угадайте язык».
                                      0
                                      Что интересно, выражение скорее всего валидно только в придуманной авторе версии C++ где "-~" это самостоятельный оператор, а не два.
                                      Яваскрипт, который вполне себе умеет и -~ и ~- конструкцию -~~--~~-2 не осиливает
                                        +1
                                        просто нужны пробелы, иначе "--" распознаётся как декремент: ideone.com/ybmRx4
                                          0
                                          А лучше скобки
                                        0
                                        — duplicate -
                                        +2
                                        Похоже на Brainfuck
                                          +10
                                          «Теперь вы можете Brainfuck прямо в C».
                                            0
                                            Обоженет
                                              +3
                                              %натурально поперхнулся чаем%
                                              0
                                              В C++ можно было и раньше, интерпретатор на шаблонах пишется на коленке. Правда, отжирает по паре гигабайт памяти на мало-мальски сложных программах, но, право же, это такая мелочь по сравнению с улучшенной читабельностью.

                                              А теперь, когда у нас есть еще и user-defined literals…
                                          0
                                          Подобный трюк очень подробно описывается в книге Ч. Петцольда «Код». Вернее, не как трюк, а как единственный разумный способ реализации разности в обычной системе записи (two’s complement) int-чисел.
                                            +1
                                            Теперь заживем.
                                              +18
                                              Кстати, очень удобно, что оператор можно использовать многократно. Теперь записывать числа стало гораздо удобнее:

                                              int x = -~-~-~-~-~0; // x = 5
                                              
                                                +9
                                                #include <stdio.h>
                                                int main(){ int $ = -~-~-~0; while ($ --> 0) putchar($["123"]); }
                                                
                                                  0
                                                  Может быть всё же «123»[$]? :)
                                                    +5
                                                    Самое смешное, что нет.

                                                    https://ideone.com/cWEr61

                                                    a[b] == b[a] == *(a + b)
                                                      +4
                                                      Черт!.. Черт!!! Совсем мозги закостенели.
                                                        +1
                                                        Хотел написать простой пример, показывающий эквивалентность этих выражений, но что-то слишком увлекся…
                                                        #include <stdio.h>
                                                        int main()
                                                        {
                                                        	int O=0,$=-~O,_[]={(++O)--,-~O++[_],-~O++[_],-~++O++[_]};
                                                        	printf("%d%c%c%c%c%d",$[_],$<<_[_[_[$]]--],O=($<<-~_[O])+-~~-~_[$],O,(-~O+$[_])>>$,_[$]);
                                                        }
                                                        
                                                          +1
                                                          Вы мне монитор сломали. Кто ответит?
                                                        +2
                                                        По стандарту эти записи эквивалентны.
                                                      0
                                                      Да вообще этот синтаксис прекрасно работает и в других ЯП. Попробовал навскидку на JS, Java, PHP, везде выводит 5.
                                                      –7
                                                      Не надо портить строгий, простой и лаконичный язык своими изобретениями.
                                                        +38
                                                        Хорошо, не будем. А C++ можно?
                                                        0
                                                        Я уж думал, вы тут расскажете про operator.(), а тут какая-то ересь :-)
                                                            +4
                                                            Легко пропатчить старые версии студии и даже gcc — просто создайте и включите вот такой хедер
                                                            #ifndef __TADPOLE_H__
                                                            #define __TADPOLE_H__
                                                            
                                                            template<class V> struct tadpole_minus {
                                                              V v;
                                                              tadpole_minus(V const& v) : v(v) {}
                                                              V operator ~ () const { return v; }
                                                            };
                                                            template<class V> struct tadpole_tilde {
                                                              V v;
                                                              tadpole_tilde(V const& v) : v(v) {}
                                                              V operator - () const { return v; }
                                                            };
                                                            
                                                            template<class V> tadpole_minus<V> operator - (V v) { return (--v); };
                                                            template<class V> tadpole_tilde<V> operator ~ (V v) { return (++v); };
                                                            
                                                            #endif // __TADPOLE_H__
                                                            

                                                            Пример
                                                            #include <iostream>
                                                            #include <string>
                                                            #include "tadpole.h"
                                                            
                                                            int main() {
                                                              std::string s = "alpha";
                                                              std::string::iterator i = s.begin() + 2;
                                                              // теперь у итератора есть операторы "головастик туда" и "головастик сюда"
                                                              std::cout << *~-~-i << *~-i << *i << *-~i << *-~-~i << std::endl;
                                                              return 0;
                                                            }
                                                            

                                                            Only users with full accounts can post comments. Log in, please.