Страницы

Поиск по вопросам

четверг, 11 октября 2018 г.

Дополнительная Специализация конструктора шаблонного класса

Пытаюсь реализовать свой дек для обучения. Встал на следующем моменте
template class 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, как я и пытался сделать изначально, следуя определению интерфейса дека.
Но наткнулся на то, что (как и было выше мне объяснено) компилятор в общем случае воспринимает конструктор
templatedeque(_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.


Ответ

Второй конструктор побеждает потому, что для первого требуется преобразование int -> unsigned int, тогда как для второго никаких преобразований не требуется. Самым простым вариантом избавления от этой проблемы, будет следующий:
template ::value, void>::type> Deque (Iterator _first, Iterator _last); //2
Но это очень грубое решение(я имею в виду проверку is_integral), желательно включать этот конструктор только для итераторов, но для этого нужно знать, что является для Вас итератором.

Комментариев нет:

Отправить комментарий