С++0x и решение проблем инициализации

    C++0xВ статьях на хабре уже касались стандарта С++0x, а в частности лямбда-выражений. Я хотел написать еще про пару изменений и дополнений, которые внесут в С++. И без лишних слов сразу к делу.


    Инициализация целочисленных переменных


    С++0x решает проблему приведения элементраных вешественных типов к целочисленным. На простейшем примере это выглядит так:

    Классический С++:

    int a = 7.3;    //Приведение к int все равно сработает
    void func(int);
    func(5.6);      //И снова сработало приведение типов без нашего разрешения



    В С++0x проблема решается с помощью введения новой конструкции для {}:

    int x1 = {7.3};    //Ошибка приведения типов - и ошибка компиляции
    double d = 7;
    int x2 {d};      //И снова нельзя, произойдет ошибка
    char x3{7};      //Все нормально
    vector<int> vi = { 1, 2.3, 4, 5.6 };  //Не получится, так как нет соответствия



    Делегирование конструкторов


    В С++ необходимо иметь два конструктора для выполнения практически одинаковых действий или использовать дополнительную функцию.

    Классический С++:

    class X
    {
      int a;
      validate(int x)
      {
        if (0<x && x<=max) a=x;
        else throw bad_X(x);
      }

    public:
      X(int x)    //Конструктор с входным параметром типа int
      {
        validate(x);
      }

      X()        //Конструктор по-умолчанию
      {
        validate(42);
      }

      X(string s)    //Конструктор с входным параметром типа string
      {
        int x = lexical_cast<int>(s);
        validate(x);
      }
      // ...
    };



    В С++0x один конструктор можно объявить в контексте другого конструктора:

    class X
    {
      int a;

    public:

      X(int x)    //Конструктор с входным параметром типа int
      {
        if (0<x && x<=max) a=x;
        else throw bad_X(x);
      }
      
      X() :X{42} { }    //Конструктор по-умолчанию
        
      X(string s) :X{lexical_cast<int>(s)} { }  //Конструктор с входным параметром типа string
      
      // ...
    }



    Нулевые указатели



    В стандартном С++ нулевой указатель представляет из себя обычный 0. В библиотеках можно встретить #define NULL 0. В С++0x вводится специальный литерал nullptr.

    Пример использования:

    char* p = nullptr;
    int* q = nullptr;
    char* p2 = 0;    //Можно и по-старинке

    void f(int);    //Функция с входным параметром int
    void f(char*);    //Функция с входным параметром char*

    f(0);        //вызов f(int) - все нормально
    f(nullptr);      //вызов f(char*) - все нормально

    void g(int);
    g(nullptr);      //Облом: nullptr не является int
    int i = nullptr;  //И снова облом



    Заключиние


    Изменения, конечно, немного косметические, но делают язык более безопасным и удобным, а главное — не выглядят так экстремально, как лямбда-выражения ).

    Продолжение следует…

    Progg it
    Поделиться публикацией
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 38
    • +1
      nullptr — это то, чего так долго всем не хватало :)

      P.S. В Boost не шарю пока что… Узнал вот только что про lexical_cast. Прикольная штука :)
      • +1
        и чем не хватало? лишних 7 символов печатать, а суть никак не изменилась
        • +8
          Во-первых, не семь. Во-вторых, более строгая проверка типов — раз; стандарт для обозначения нулевых указателей — два (а то пишут… кто 0, кто NULL, а кто-то вообще MY_OWN_SUPERMEGAMACROS_4_NULL_POINTERS).
          • –1
            Интересно, за что минус =)))
            • 0
              О, теперь всё норм :)

              Рейтинг ничто — правда всё!
            • 0
              А вы уверены, что NULL != nullptr? Если это не так, тогда все ваши выгоды выеденного яйца не стоят. Кроме того, чем вам 0 не нравится? Вполне ясно, что указатель неправильный, не вижу разницы между !ptr и ptr == nullptr, к тому же первое еще и явно нагляднее.
              • 0
                Тем что 0 имеет тип int, только и всего.
                • –2
                  Значение указателя — это адрес, который так же имеет тип int, что не так?
                  • 0
                    И все же, указатель — это не просто int… Не всегда удобно интерпретировать его как простое целочисленное значение.
                    • –5
                      Блин, а что же это еще, если не int? А как же его еще можно интерпретировать, если не int?
                      Даже учитывая комментарий ниже, всё равно это int, в любом случае.

                      P.S. «Не просто int» — это нечто.
                      • 0
                        К указателям удобно применять арифметику указателей, а не арифметику интов:
                        Если я прибавляю к указателю на начало массива еденицу, то я сдвигаю указатель на размер элемента массива, а не на еденицу.
                        • –5
                          Ну и что? Ну а что от этого меняется? Только лишь характер операций? От этого указатель не перестаёт быть int-ом.
                          Кроме того, возвращаясь к теме выше, даже учитывая специфику операций, чем вам мешает 0? От того, что я присвою 0 указателю нарушится механика операций? Или 0 не является адресом? Да всё равно тот же nullptr будет тем же 0-ём, какая разница?
                          • +1
                            Ну, так всё можно интами представлять о_О

                            Просто null и 0 это разные сущности: 0 — число, null — указатель. Вы ведь не храните инты как чары?
                    • 0
                      А в x64, а сегменты, а различные платформы,…
                      • +3
                        Это утверждение неверно. Размеры указателей и int не определены и в общем случае между собой не связаны.
                        Словом, это два совершенно разных типа.
                        • +1
                          Логически NULL — это не адрес, а его отсутствие :) Указатель указывает в никуда (или, точнее, никуда не указывает), а не на нулевой байт. Просто так сложилось что обращаться по нулевому адресу нельзя нельзя, вот NULL и равен 0, а вот если бы мы могли использовать все пространство памяти…
                  • +2
                    изменилось, теперь не будет ошибок с присвоением NULL'а обычной переменной, nullptr можно будет присовить только указателю.
                  • +2
                    Буст вообще вся прикольная штука. Но только для шарящих, а то такого можно наверетенить.
                  • +2
                    А почему не null?
                    • +2
                      Надо же выделиться как-то. После их синтаксиса лямбд простой null смотрелся бы слишком банально.

                      Хотя, и это не получилось — nullptr уже давно есть в C++/CLI
                      • +1
                        null уже использовался некоторыми библиотеками. Для совместимости со старым кодом взяли более редкое слово.
                    • 0
                      насколько я понял из текущего доступного драфта, это так и не починили:
                      struct A{
                        int a[3];
                        A(): a({1,2,3}){};
                      };
                      <pre>
                      • –1
                        мда, видимо народ ничего кроме nullptr использовать не собирается, и зачем им с++, пешите на чистом си :)
                        • 0
                          Это Вы так решили из-за того, что Ваш коммент обделили вниманием? :)
                          • 0
                            не, изза того что никого не интересует проблема инициализации массивов в конструкторе :)
                            • 0
                              Вообще, возможно, было бы удобно делать так, как Вы написали. А что, по новому стандарту нельзя, да?
                              • 0
                                по старому стандарту фигурные скобки в списке инициализации конструктора недопустимы, вот по новому непонятно…
                      • +1
                        Спасибо, большое! Жду когда же допилят gcc, чтобы использовать на полную мощь.
                        • 0
                          Отлично, автор пиши больше про новый стандарт. За основу можешь взять эту ветку: forum.sources.ru/index.php?showtopic=190375
                          Там много букв, так что самая выжимка не помешает.
                          • +2
                            Полезные изменения, хотя новые действия с фигурными скобками запутывают.
                            • +2
                              Да, количество скобок C+0x зашкаливает :) Со временем он, наверное, эволюционирует в в LISP. :)
                              • 0
                                К этому времени Бьёрн будет стареньким-стареньким :))
                            • 0
                              int x1 = {7.3};    //Ошибка приведения типов - и ошибка компиляции
                              double d = 7;
                              int x2 {d};      //И снова нельзя, произойдет ошибка
                              char x3{7};      //Все нормально
                              vector<int> vi = { 1, 2.3, 4, 5.6 };  //Не получится, так как нет соответствия
                              

                              Разьясните, а зачем нужно так писать? Чтобы специально получить ошибку компиляции? А если не хочешь получить, писать по старому? :-)
                              Странное какое-то решение… Если бы такая более строгая типизация вводилась принудительно в весь язык, то было бы понятно, а так ведь все просто будут писать по-старому (хотя бы для совместимости).
                              Если я всё верно понял.
                              • 0
                                поддерживаю, надеюсь разработчики компиляторов сделают опцию что по умолчанию будет считаться что со скобками написано.
                                • 0
                                  Как раз, скорее, старый вариант оставили для совместимости, а писать начнут по-новому… Ну, естественно, не в тех проектах, которые по 20 лет уже пишутся.
                                  • 0
                                    К примеру использовать фигурные скобки можно при создании собственной библиотеки шаблонов, а при последующем использовании этих шаблонов, часто встречаются ситуации, когда тип подставляется на этапе компиляции. Так, чтобы не произошло необоснованного преобразования типов — такое как раз и подойдёт.

                                    П.С. Ошибка замеченная на этапе компиляции — это в некотором смысле хорошая ошибка :)
                                  • 0
                                    В C++ когда-нибудь модули будут? Или так и дальше всё инклудами?
                                    • 0
                                      Что вы называете модулем?
                                      Это еденица уровня инклуда, или уровня статической/динамической библиотеки, или нечто среднее?

                                      ЗЫ: не хочу обидеть, но мне кажется вы не так давно мигрировали с паскаля или делфи на с++, выучили синтаксис недоразобравшись в философии языка и теперь его критикуете :)

                                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.