#cpp #visual_cpp #шаблоны_с++
Хочу заставить работать цикл for(auto c:s) для обычной строки в духе C (0-завершенной). Работаю с Visual C++ 2015. Попытался специализировать шаблон begin(): template<> inline const char* begin(const char*& _Cont) noexcept { return _Cont; } Увы, фокус не удался - ошибка C2912, явная специализация (см. выше) не является специализацией функции-шаблона. Соответственно, вопрос первый - почему? Если просто написать перегрузку inline const char* begin(const char*& Cont) { return Cont; } ничуть не лучше - ошибка C3312, не найдена вызываемая функция begin для типа const char*. Замена в перегрузке begin(const char*& Cont) на begin(const char* Cont), само собой, не спасает. Вопрос второй - вроде, по всем канонам, при разрешении шаблонов и перегрузки должна быть выбрана перегрузка? Или здесь не просто вызов функции и это правило не работает? Ну, и третий вопрос - это вообще можно, то, что я хочу, или нет? и если да - то как?
Ответы
Ответ 1
Всё ведь совсем просто, не понимаю зачем Вы полезли в дебри шаблонов: #includeconst char* begin(const char* str) noexcept { return str; } const char* end(const char* str) noexcept { int count = strlen(str); return str + count; } int main() { for(auto c : "Hello Moscow!") std::cout << c; std::cout << "\n"; } На деле оказалось не так просто. Пример выше будет работать даже если убрать мою реализацию begin/end(т.е. она не используется). Видимо есть перегрузка для массивов в std для begin/end, а вот чтобы for заработал для const char* нужно написать следующий код: #include namespace std { const char* begin(const char* str) noexcept { return str; } const char* end(const char* str) noexcept { int count = strlen(str); return str + count; } } int main() { const char* str = "Hello Moscow!"; for(auto c: str) std::cout << c; std::cout << "\n"; } Но с этим кодом есть проблема: стандарт явно запрещает засорять namespace std подобными реализациями. Поэтому, в силу чисто академического интереса можно и посмотреть на это, но вот использовать лучше не стоит(хотя Вы вряд ли нарвётесь на неприятности). По другому сделать не получится, потому как for внутри себя, скорее всего, использует std::begin/std::end Обновление 2: всё вышесказанное относится лишь к студии. В других компиляторах это не работает, а это значит, что std::begin/std::end могут быть вызваны в студии, но для этого кода они не будут вызваны ни в clang ни в gcc. Что это всё значит? Это значит, что for для const char* работать не будет. Обновление 3: Почему не работает попытка специализации шаблона(как в вопросе) внутри namespace std?(из комментариев). Дело в том, что в заголовках студии есть следующие перегруженные функции для std::begin: Раз template auto inline begin(_Container& _Cont) -> decltype(_Cont.begin()) Два template auto inline begin(const _Container& _Cont) -> decltype(_Cont.begin()) И три template inline_CONST_FUN _Ty *begin(_Ty (&_Array)[_Size]) _NOEXCEPT Так вот, написав: template<> inline const char* begin(const char*& _Cont) noexcept Мы начинаем проверять, какой шаблон мы специализируем. Начнём с первого — проверка не проходит, почему? Потому что decltype(_Cont.begin()) не скомпилируется, т.к. у const char* нет метода begin(), поэтому срабатывает SFINAE и вариант отбраковывается. То же происходит и со вторым вариантом. Остается третий вариант, который принимает массив и из него извлекат 2 необходимых параметра шаблона, но у нас ведь указатель, поэтому этот вариант тоже отбраковывается. И у нас остается ... ничего! Вот и ошибка компилятора, что специализация не имеет общего случая, который специализировать. Приведу пример, как можно сделать специализацию для первого и третьего случая: #include #include #include namespace std { template<> int *begin(int(&_Array)[50]) noexcept { std::cout << "Yeah!\n"; return nullptr; } template<> auto inline begin(const std::vector & _Cont) -> decltype(_Cont.begin()) { std::cout << "Beah!\n"; return _Cont.begin(); } } int main() { int a[50]; std::begin(a); std::begin(std::vector {}); } Но этот код не компилируется 2015 студией. Причина мне не ясна(ей не нравится специализация для контейнера), видимо баг в студии(создал bug-report), т.к. другие компиляторы такой код компилируют на ура(для clang надо будет поправить noexcept).
Комментариев нет:
Отправить комментарий