Пытаюсь реализовать свой дек для обучения.
Встал на следующем моменте
template
public:
Deque ( const unsigned &, const T & = T() ); //1
template
};
В main.cpp идёт создание дека:
Deque
Все хорошо если нет 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
Т.е. видно что в 11-м стандарте (как и описывалось в сообщениях выше) используется подход метапрограммирования и шаблон, который определяет «подключать» ли данный вызов - _RequireInputIter
В 98м же Си++ используется вызов на прямую, без таких вот конструкций, типа _RequireInputIter, как я и пытался сделать изначально, следуя определению интерфейса дека.
Но наткнулся на то, что (как и было выше мне объяснено) компилятор в общем случае воспринимает конструктор
template
как более предпочтительный (т.к. из за наличия шаблонов, в него можно «всунуть» любой тип сразу, не задействуя преобразования типов) нежели конструктор
deque(size_type __n, const value_type& __value = value_type(), const allocator_type& __a = allocator_type())
Но и, собственно говоря, в реализации STL для си++98 все так и получается, как получалось у меня на момент написания первого сообщения в этой теме..
Такой вызов
std::deque
идёт непосредственно к конструктору ----- #2
А вот такой
std::deque
, к конструктору ----- #1
Т.е. можно говорит, что до появления std::enable_if - конструктор ----- #1 почти не использовался, а лишь служил описанием интерфейса.
А его работу исполнял ----- #2, используя для своего «двойного» поведения метод
_M_initialize_dispatch(__first, __last, _Integral());
Который, в случае если вызов объекта _Integral() имеет тип __true_type - вызывает ф-ю _M_initialize_dispatch, которая, рассмартивает первые два параметра как простые типы - первый тип размера, второй тип заполняемого значения,
Если же при инстанцировании
__is_integer
InputIterator не попадает ни в одну из перегруженных для __is_integer специализаций, то объект _Integral() имеет тип __false_type
И в таком случае используется перегруженная версия
_M_initialize_dispatch
, которая, переданные в неё параметры воспринимает как итераторы а не базовые типы.
И вот, таким вот образом, один конструктор, (с другим интрефейсом и как бы созданный совсем для другого) эмулирует действия первого
Убедиться в этом можно, заккоментив stl_deque.h пару строк
template
и выполнив код
std::deque
собранный для -std=c++11 и -std=c++98.
Ответ
Второй конструктор побеждает потому, что для первого требуется преобразование int -> unsigned int, тогда как для второго никаких преобразований не требуется. Самым простым вариантом избавления от этой проблемы, будет следующий:
template
Но это очень грубое решение(я имею в виду проверку is_integral), желательно включать этот конструктор только для итераторов, но для этого нужно знать, что является для Вас итератором.
Комментариев нет:
Отправить комментарий