На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
  
> explicit для чего нужен?
    Часто в исходниках видел перед методами служебное слово explicit, скажите, пожалуйста, зачем оно нужно и что оно делает?
      Запрещает неявное преобразование
      Используется в конструкторах с одним параметром.
      Сообщение отредактировано: Крон -
        Цитата Крон @
        Используется в конструкторах с одним парамтером.

        При чём тут один параметр? Используется для того, чтобы конструктор задавали явно.
          А как можно неявно задать конструктор с более чем один параметром?
          Сообщение отредактировано: Крон -
            Цитата Крон @
            А как можно неявно задать конструктор с более чем один параметром?

            ExpandedWrap disabled
              class A
              {
                A(int first, int second);
                ~A();
              };
              Цитата Алкаш @
              Используется для того, чтобы конструктор задавали явно.

              Ок, а что такое по твоему "задать конструктор"? :)


              DelphiLexx,
              ExpandedWrap disabled
                struct A
                {
                    A(int) { }
                };
                struct B
                {
                    explicit B(int) { }
                };
                 
                int main()
                {
                    A a1(5); // ok
                    B b1(5); // ok
                    A a2 = 10; // ok
                    B b2 = 10; // error  <<<==========================
                }
                Цитата archimed7592 @
                Ок, а что такое по твоему "задать конструктор"? :)

                Цитата archimed7592 @
                B b1(5); // ok
                  Цитата Алкаш @
                  Цитата Крон @
                  Используется в конструкторах с одним парамтером.

                  При чём тут один параметр? Используется для того, чтобы конструктор задавали явно.


                  Интересно а для чего использовать тогда explicit c конструктором который имеет более чем один параметр? Я просто не вижу смысла потому что приведение и так неявное невозможно. А archimed7592 уже ответил на вопрос полностъю я считаю.
                  Сообщение отредактировано: Craft -
                    Цитата Craft @
                    Интересно а для чего использовать тогда explicit c конструктором который имеет более чем один параметр?

                    ExpandedWrap disabled
                      struct S {
                       /*explicit*/ S(int, int = 0) {}
                      };
                       
                      int main() {
                       S s = 10;
                      }

                    раскомментируй explicit
                      ExpandedWrap disabled
                        class Foo
                        {
                        public:
                              Foo(const std::string & sString, bool Flag = true);
                              Foo(bool Flag);
                        ...
                        };


                      Вот пример такой класс, а теперь прикол :
                      ExpandedWrap disabled
                        Foo("foo");

                      Вызывется 2-ой конструктор, а не первый. Понятно что есть небольшая ошибка, что ведь нужен обьект типа string, а я передаю const char * - это простая ошибка которая может возникнуть в процессе кодирования, когда нету autocomplete и программист уже устал.
                      Если сделать оба конструктора explicit, такой ошибки не будет и компилятор предупредит об этой ошибки с неявным преобразованием. На эту ошибку наткнулся в реальном коммерческом проекте.
                      Вывод : всегда делать конструкторы explicit, если у него есть параметры.
                      Сообщение отредактировано: Cechmanek -
                        Alek86 Я не имел ввиду что конструктор принимает значения по умолчанию. Так можна и 10 аргументов инициализировать по умолчанию. Ты же инициализуеш неявно только первый аргумент, а остальныи присваиваеш значение по умолчанию. Я знаю как работает explicit и для чего он нужен.
                        Допустим без explicit у тебя конструктор автоматически определил бы преобразования из int в твой клас S, ну создаст он стек на 10 елементов. Да не спорю это не то что мы ожидаем. Но первая переменная у тебя всё равно получит значение 10. Так работает неявное преобразование. Большой минус только что стек сильно увеличивается. Это неправильный стиль я не спорю. Но чтоб считать это такой уж ошибкой я не вижу смысла. Программист должен знать что он делает в первую очередь. Пусть хоть на миллион элементов неявно инициализацию использует. Лиш бы ему памъяти хватило чтоб небыло переполнения. Просто насколько мне не изменяет памъять то у windows под кучю выделяется 1Мб так что если не жалко израсходованой памъяти то можна такой метод использовать. Я никогда не видел что ктото использовал неявное преобразование на подобие этого
                        ExpandedWrap disabled
                          S s = 10;
                        кроме тестовых каких нибуть примеров. Начинающий программист в основном не использует explicit, а опытный программист просто напросто никогда не напишет такой код который я написал више. Так что особого кризиса как такового и нету ;)

                        P.S. Если чесно, то я не подумал, когда писал ответ, про возможность передачи значения по умолчанию.

                        ExpandedWrap disabled
                          Cechmanek
                        Неплохой пример. Но у тебя ошибка у примере. Даже если ты напишеш два конструктора как explicit через такую запись ти не увидиш ошибку компиляции.
                        ExpandedWrap disabled
                          Foo("foo");

                        Разве что через такую
                        ExpandedWrap disabled
                          Foo p = "foo";
                        а то что ты описал это явное преобразование. И никакой ошибки ты не получиш даже если конструктор объявлен как explicit. Посмотри на пример archimed7592
                        Цитата
                        B b1(5); // ok
                        Сообщение отредактировано: Craft -
                          Цитата Cechmanek @
                          Вызывется 2-ой конструктор, а не первый.

                          Это потому что последовательность преобразований из массива char в bool - стандартная, а в std::string - пользовательская. При разрешении перегрузки предпочтение отдаётся последовательности стандартных преобразований.

                          Цитата Cechmanek @
                          Если сделать оба конструктора explicit

                          то в этом плане ничего не изменится.

                          Цитата Cechmanek @
                          Вывод : всегда делать конструкторы explicit, если у него есть параметры.

                          Плохой вывод.
                          Сообщение отредактировано: Dantes -
                            Один из способов из способов избежать создания временных объектов это по возможности использовать объявления конструкторов как
                            ExpandedWrap disabled
                              explicit
                            . :)
                            Вот и все. 8-)
                            Сообщение отредактировано: vovan01 -
                              Цитата Craft @
                              Начинающий программист в основном не использует explicit, а опытный программист просто напросто никогда не напишет такой код который я написал више.

                              ты считаешь, что опытный программист - это безошибочная машина для написания кода? огорчяу тебя - даже "сеньоры" с огромным стажем работы ошибаются и достаточно нередко - специфика работы такая, что всего умом не охватишь (у макконелла написано как от этого максимально откреститься (путем упрощения кода) но на практике так круто никто не пишет :( )
                              потому и придуманы специальные "ограничители", чтобы ошибьки не так плодились. типа приватных членов класса, приватного наследования, explicit, попытались добавить спецификацию throw функции и т.п.

                              и потому же рекомендуют пореже писать кастомных операторов преобразования

                              насчет правила "ставить explicit всегда и везде" не согласен, но только потому что не обжигался на этом "в реале" :)
                              я ставлю его только тогда, когда "чувствую" что тут его лучше поставить
                              Сообщение отредактировано: Alek86 -
                                Craft и Archimed раскрыли тему полностью :yes:

                                Цитата
                                Один из способов из способов избежать создания временных объектов это по возможности использовать объявления конструкторов как

                                временные объекты здесь вообще не при чём.
                                  Цитата Craft @
                                  Просто насколько мне не изменяет памъять то у windows под кучю выделяется 1Мб

                                  Жжёшь :lool:

                                  Цитата Craft @
                                  Я никогда не видел что ктото использовал неявное преобразование на подобие этого
                                  ExpandedWrap disabled
                                    S s = 10;
                                  кроме тестовых каких нибуть примеров. Начинающий программист в основном не использует explicit, а опытный программист просто напросто никогда не напишет такой код который я написал више.

                                  Тебя послушать, так шаблон std::complex создан на коленках начинающими программистами :lol:
                                    Цитата vovan01 @
                                    Один из способов из способов избежать создания временных объектов это по возможности использовать объявления конструкторов как

                                    Глупость.
                                      Цитата Бобёр @
                                      Craft и Archimed раскрыли тему полностью :yes:

                                      Цитата
                                      Один из способов из способов избежать создания временных объектов это по возможности использовать объявления конструкторов как

                                      временные объекты здесь вообще не при чём.

                                      лучше почитай литературу, могу дать ссылку на книжку ;)

                                      а это другой сучий когда можно использовать explicit

                                      As a rule, every constructor that takes a single argument, including constructors that take multiple arguments with default values such as the following, should be explicit, unless you have a good reason to allow implicit conversions:
                                      ExpandedWrap disabled
                                        class File
                                        {
                                        public:
                                         //this cоnstructor may be called with a single argument
                                         //it's therefore declared explicit:
                                         explicit File(const char *name,
                                                ios_base::openmode mode=ios_base::out,
                                                long protection = 0666);
                                        };
                                      Сообщение отредактировано: vovan01 -
                                        и где тут встечаются слова "временные объекты"?
                                        тут видны слова "implicit conversions" - о чем все и говорят
                                        Сообщение отредактировано: Alek86 -
                                          Цитата vovan01 @
                                          лучше почитай литературу, могу дать ссылку на книжку ;)

                                          Редко встречаю такой аргумент, но всегда ему умиляюсь :rolleyes:. С тем же успехом и тебе можно посоветовать "почитать литературу".
                                            предлагаю с компилить следующий код
                                            ExpandedWrap disabled
                                              #include <cstdlib>
                                              #include <iostream>
                                               
                                              using namespace std;
                                              template<typename T >
                                              class ObjectBase
                                              {
                                                    public :
                                                            ObjectBase(){
                                                                        cout <<"In constructor_1"<<endl;
                                                                        }
                                                            ObjectBase(T ob): i(ob){
                                                                        cout <<"In constructor_2"<<endl;
                                                                                   }
                                                                                    
                                                           void GetId(){
                                                                        cout<< i <<endl;
                                                                       }
                                                           ~ObjectBase(){
                                                                        cout <<"In destructor"<<endl;
                                                                         }            
                                                    private:
                                                            T i;
                                                                  
                                              };
                                              template<typename U>
                                              void f( ObjectBase<U> const &o)
                                              {
                                                    cout <<"In funstion f"<<endl;
                                               }
                                              int main(int argc, char *argv[])
                                              {
                                                  cout<<"In main"<<endl;
                                                  
                                                  ObjectBase<int> obj_1;
                                                  
                                                  obj_1 = 10;
                                                  
                                                  obj_1.GetId();
                                                  
                                                  f(ObjectBase<int>());
                                                  
                                                  system("PAUSE");
                                                  return EXIT_SUCCESS;
                                              }


                                            а после этого по пробуйте вот это
                                            ExpandedWrap disabled
                                              #include <cstdlib>
                                              #include <iostream>
                                               
                                              using namespace std;
                                              template<typename T >
                                              class ObjectBase
                                              {
                                                    public :
                                                          explicit ObjectBase(){
                                                                        cout <<"In constructor_1"<<endl;
                                                                        }
                                                          explicit ObjectBase(T ob): i(ob){
                                                                         cout <<"In constructor_2"<<endl;
                                                                                   }
                                                                                    
                                                           void GetId(){
                                                                        cout<< i <<endl;
                                                                        }
                                                           ~ObjectBase(){
                                                                         cout <<"In destructor"<<endl;
                                                                         }            
                                                    private:
                                                            T i;
                                                                  
                                              };
                                              template<typename U>
                                              void f( ObjectBase<U> const &o)
                                              {
                                                    cout <<"In funstion f"<<endl;
                                               }
                                              int main(int argc, char *argv[])
                                              {
                                                  cout<<"In main"<<endl;
                                                  
                                                  ObjectBase<int> obj_1(10);
                                                      
                                                  obj_1.GetId();
                                                  
                                                  f(ObjectBase<int>());
                                                  
                                                  system("PAUSE");
                                                  return EXIT_SUCCESS;
                                              }


                                            Теперь сравните результаты думаю что будет ясно что преобразование
                                            ExpandedWrap disabled
                                               obj_1 = 10;

                                            создаёт временный объект. :whistle:
                                              vovan01, а ты попробуй во втором случае убрать explicit и скомпилировать, ага?
                                              С тем же успехом можно было в первом варианте написать return 0, а во втором - return EXIT_SUCCESS и заявить, что EXIT_SUCCESS помогает избежать временных объектов. explicit, как и EXIT_SUCCESS к временным объектам никакого отношения не имеет.
                                                Цитата archimed7592 @
                                                vovan01, а ты попробуй во втором случае убрать explicit и скомпилировать, ага?
                                                С тем же успехом можно было в первом варианте написать return 0, а во втором - return EXIT_SUCCESS и заявить, что EXIT_SUCCESS помогает избежать временных объектов. explicit, как и EXIT_SUCCESS к временным объектам никакого отношения не имеет.

                                                ты просто не понял ничего если оставить линию
                                                ExpandedWrap disabled
                                                  obj_1 = 10;

                                                то по просту у тебя во втором случае компилятор выдаст ошибку по этому я и написал.
                                                ExpandedWrap disabled
                                                  ObjectBase<int> obj_1(10);


                                                archimed7592 - так ты убедился что в первом случае создаётся временный объект?
                                                Сообщение отредактировано: vovan01 -
                                                  Цитата vovan01 @
                                                  ты просто не понял ничего

                                                  Куда уж мне :rolleyes:.

                                                  Цитата vovan01 @
                                                  если оставить линию
                                                  obj_1 = 10;

                                                  то по просту у тебя во втором сучий компилятор выдаст ошибку по этому я и написал.
                                                  ObjectBase<int> obj_1(10);

                                                  Вы делаете мне смешно :lol:.
                                                  Почему ты не заменил эту строку на return 0; ?
                                                  Из твоих слов получается, что присваивание и конструирование - это одно и то же. Что ж, тогда я заявляю, что присваивание и return 0 - это одно и то же ;).

                                                  Если класс подразумевает операцию присваивания, то тебе придётся её реализовать и explicit никак на это не повлияет. Если ты захочешь реализовать эту операцию "без временных объектов" - ты её реализуешь. Не захочешь - будут временные объекты(или их не будет - это уже компилятору виднее, стандарт ему руки развязывает). При чём здесь explicit?

                                                  Класс с операцией присваивания int и класс без таковой операции - это два разных класса, не надо подавать их как один и тот же класс с разным выходом временных объеков.

                                                  Цитата vovan01 @
                                                  archimed7592 - так ты убедился что в первом случай создаётся временный объект?

                                                  Делать мне больше нечего.
                                                    archimed7592
                                                    Цитата archimed7592 @
                                                    Почему ты не заменил эту строку на return 0; ?


                                                    я тебе про Фому а ты мне про Ерему.

                                                    Слушай я прогу написал в IDE Dev-C++, если ты слыхал про такую, там EXIT_SUCCESS = 0
                                                      vovan01, сравни:
                                                      ExpandedWrap disabled
                                                        ObjectBase<int> obj_1;
                                                        obj_1 = 10;

                                                      и
                                                      ExpandedWrap disabled
                                                        ObjectBase<int> obj_1(10);

                                                      Мне почему-то не кажется, что это одно и то же. И при чем тут explicit?
                                                      ЗЫ Да и копировщик добавить бы не мешало
                                                      Сообщение отредактировано: pan2004 -
                                                        Затянувшаяся жара определённо плохо влияет на некоторых людей.
                                                          Цитата pan2004 @
                                                          Мне почему-то не кажется, что это одно и то же. И при чем тут explicit?
                                                          ЗЫ Да и копировщик добавить бы не мешало

                                                          Ну, автор примера имел в виду, что в первом случае для присваивания будет сначала создан временный объект (путем вызова соответствующего конструктора), после чего будет собственно присваивание. А во втором случае, если просто добавить explicit, то присваивание не пройдет:
                                                          ExpandedWrap disabled
                                                            hellow_world.cpp:37: error: no match for 'operator=' in 'obj_1 = 10'
                                                            hellow_world.cpp:7: note: candidates are: ObjectBase<int>& ObjectBase<int>::operator=(const ObjectBase<int>&)

                                                          т. к. не будет выполнено неявное приведение 10-ти к ObjectBase.
                                                          1 пользователей читают эту тему (1 гостей и 0 скрытых пользователей)
                                                          0 пользователей:


                                                          Рейтинг@Mail.ru
                                                          [ Script execution time: 0,0759 ]   [ 15 queries used ]   [ Generated: 9.07.25, 14:23 GMT ]