Хочу заставить работать цикл 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
Два
template
И три
template
Так вот, написав:
template<>
inline const char* begin(const char*& _Cont) noexcept
Мы начинаем проверять, какой шаблон мы специализируем. Начнём с первого — проверка не проходит, почему? Потому что decltype(_Cont.begin()) не скомпилируется, т.к. у const char* нет метода begin(), поэтому срабатывает SFINAE и вариант отбраковывается. То же происходит и со вторым вариантом. Остается третий вариант, который принимает массив и из него извлекат 2 необходимых параметра шаблона, но у нас ведь указатель, поэтому этот вариант тоже отбраковывается. И у нас остается ... ничего! Вот и ошибка компилятора, что специализация не имеет общего случая, который специализировать.
Приведу пример, как можно сделать специализацию для первого и третьего случая:
#include
namespace std
{
template<>
int *begin(int(&_Array)[50]) noexcept
{
std::cout << "Yeah!
";
return nullptr;
}
template<>
auto inline begin(const std::vector
";
return _Cont.begin();
}
}
int main()
{
int a[50];
std::begin(a);
std::begin(std::vector
Но этот код не компилируется 2015 студией. Причина мне не ясна(ей не нравится специализация для контейнера), видимо баг в студии(создал bug-report), т.к. другие компиляторы такой код компилируют на ура(для clang надо будет поправить noexcept).
Комментариев нет:
Отправить комментарий