Страницы

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

суббота, 4 января 2020 г.

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

#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



Всё ведь совсем просто, не понимаю зачем Вы полезли в дебри шаблонов: #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 << "\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).

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

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