#cpp #шаблоны_с++ #function_overloading
Пытаюсь реализовать свой дек для обучения. Встал на следующем моменте templateclass Deque { private: enum { defaultSize = 25, len = 5 }; void dealloc ( ); int alloc ( const unsigned & = defaultSize, const T & = T() ); T ** array; pair xy; ... public: Deque ( const unsigned &, const T & = T() ); //1 template Deque (Iterator _first, Iterator _last); //2 Deque ( ); virtual ~Deque(); }; В main.cpp идёт создание дека: Deque test1(25, -1); Все хорошо если нет 2-го конструктора. Второй конструктор "перебивает" действие первого. Почему так происходит я не понимаю, ведь в конструктор передаются типы const unsigned и const int и как я понимаю компилятору следовало бы направить этот путь к первому конструктору, но он упорно использует второй... Подскажите пожалуйста что не так. UPDATE: Ещё раз спасибо, всем кто ответил, разобрался и узнал новое. Позволю себе добавить некоторые уточнения, которые узнал, и которые, возможно покажутся интересными и другим. В частности, это уточнение комментария: " std::enable_if пишется за 10 минут на C++98, просто с C++11 это часть стандарта. SFINAE доступно довольно давно, поэтому, наиболее вероятно, как-то так и было сделано раньше. " Поехали: в файле /usr/include/c++/4.9/bits/stl_deque.h, где-то в районе 899 строки будет: #if __cplusplus >= 201103L deque(size_type __n, const value_type& __value, const allocator_type& __a = allocator_type()) : _Base(__a, __n) { _M_fill_initialize(__value); } #else explicit deque(size_type __n, const value_type& __value = value_type(), const allocator_type& __a = allocator_type()) : _Base(__a, __n) { _M_fill_initialize(__value); } /*--------------- #1 */ #endif #if __cplusplus >= 201103L template > deque(_InputIterator __first, _InputIterator __last, const allocator_type& __a = allocator_type()) : _Base(__a) { _M_initialize_dispatch(__first, __last, __false_type()); } #else template deque(_InputIterator __first, _InputIterator __last, const allocator_type& __a = allocator_type()) : _Base(__a) { // Check whether it's an integral type. If so, it's not an iterator. typedef typename std::__is_integer<_InputIterator>::__type _Integral; _M_initialize_dispatch(__first, __last, _Integral()); } /*--------------- #2 */ #endif Т.е. видно что в 11-м стандарте (как и описывалось в сообщениях выше) используется подход метапрограммирования и шаблон, который определяет «подключать» ли данный вызов - _RequireInputIter В 98м же Си++ используется вызов на прямую, без таких вот конструкций, типа _RequireInputIter, как я и пытался сделать изначально, следуя определению интерфейса дека. Но наткнулся на то, что (как и было выше мне объяснено) компилятор в общем случае воспринимает конструктор template deque(_InputIterator _first, _InputIterator __last, const allocator_type& __a = allocator_type()) как более предпочтительный (т.к. из за наличия шаблонов, в него можно «всунуть» любой тип сразу, не задействуя преобразования типов) нежели конструктор deque(size_type __n, const value_type& __value = value_type(), const allocator_type& __a = allocator_type()) Но и, собственно говоря, в реализации STL для си++98 все так и получается, как получалось у меня на момент написания первого сообщения в этой теме.. Такой вызов std::deque dq (10,-1) идёт непосредственно к конструктору ----- #2 А вот такой std::deque dq (10u,-1) , к конструктору ----- #1 Т.е. можно говорит, что до появления std::enable_if - конструктор ----- #1 почти не использовался, а лишь служил описанием интерфейса. А его работу исполнял ----- #2, используя для своего «двойного» поведения метод _M_initialize_dispatch(__first, __last, _Integral()); Который, в случае если вызов объекта _Integral() имеет тип __true_type - вызывает ф-ю _M_initialize_dispatch, которая, рассмартивает первые два параметра как простые типы - первый тип размера, второй тип заполняемого значения, Если же при инстанцировании __is_integer ::__type InputIterator не попадает ни в одну из перегруженных для __is_integer специализаций, то объект _Integral() имеет тип __false_type И в таком случае используется перегруженная версия _M_initialize_dispatch , которая, переданные в неё параметры воспринимает как итераторы а не базовые типы. И вот, таким вот образом, один конструктор, (с другим интрефейсом и как бы созданный совсем для другого) эмулирует действия первого Убедиться в этом можно, заккоментив stl_deque.h пару строк template void _M_initialize_dispatch(_Integer __n, _Integer __x, __true_type) { //_M_initialize_map(static_cast (__n)); //_M_fill_initialize(__x); } и выполнив код std::deque dq (10,-1); std::copy ( dq.begin(), dq.end(), std::ostream_iterator (std::cout, " ")); собранный для -std=c++11 и -std=c++98.
Ответы
Ответ 1
Второй конструктор побеждает потому, что для первого требуется преобразование int -> unsigned int, тогда как для второго никаких преобразований не требуется. Самым простым вариантом избавления от этой проблемы, будет следующий: template::value, void>::type> Deque (Iterator _first, Iterator _last); //2 Но это очень грубое решение(я имею в виду проверку is_integral), желательно включать этот конструктор только для итераторов, но для этого нужно знать, что является для Вас итератором.
Комментариев нет:
Отправить комментарий