Страницы

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

понедельник, 4 февраля 2019 г.

Как написать специализацию begin() для C-строки?

Хочу заставить работать цикл 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), само собой, не спасает.
Вопрос второй - вроде, по всем канонам, при разрешении шаблонов и перегрузки должна быть выбрана перегрузка? Или здесь не просто вызов функции и это правило не работает?
Ну, и третий вопрос - это вообще можно, то, что я хочу, или нет? и если да - то как?


Ответ

Всё ведь совсем просто, не понимаю зачем Вы полезли в дебри шаблонов:
#include
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() { for(auto c : "Hello Moscow!") std::cout << c; std::cout << "
"; }

На деле оказалось не так просто. Пример выше будет работать даже если убрать мою реализацию 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 << "
"; }
Но с этим кодом есть проблема: стандарт явно запрещает засорять 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!
"; return nullptr; }
template<> auto inline begin(const std::vector& _Cont) -> decltype(_Cont.begin()) { std::cout << "Beah!
"; return _Cont.begin(); } }
int main() { int a[50]; std::begin(a); std::begin(std::vector{}); }
Но этот код не компилируется 2015 студией. Причина мне не ясна(ей не нравится специализация для контейнера), видимо баг в студии(создал bug-report), т.к. другие компиляторы такой код компилируют на ура(для clang надо будет поправить noexcept).

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

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