Предпочитаю какую-то функцию над ADL

Я хочу использовать диапазон на основе for перебирать кодовые точки Юникода в кодировке UTF8 std::string, Я определил свой begin а также end в глобальном пространстве имен, кроме begin а также end в std пространство имен является предпочтительным (то есть найденное ADL). Есть ли способ отдать предпочтение моим собственным функциям?

Пример:

const char* begin(const std::string& s) {
std::cout << "BEGIN";
return s.data();
}

const char* end(const std::string& s) {
std::cout << "END";
return s.data() + s.length();
}

int main() {
std::string s = "asdf";

for (char c : s)
std::cout << c;
}

Я хочу это напечатать BEGINENDasdf (или же ENDBEGINasdf) но это печатает asdf,

Есть ли другой способ, кроме как сделать руководство for используя квалифицированное имя?

7

Решение

Заворачивать std::string в вашем собственном типе. Сделав его шаблоном, вы можете настроить любой существующий контейнер и добавить к нему собственную логику диапазона. Это даже не отличается от вашей первой попытки.

#include <string>
#include <iostream>

template <typename S>
struct custom_container {
S &s_;

custom_container (S &s) : s_(s) {}

auto begin() -> decltype(s_.begin()) {
std::cout << "BEGIN";
return s_.begin();
}

auto end() -> decltype(s_.end()) {
std::cout << "END";
return s_.end();
}
};

template <typename S>
custom_container make_container (S &s) {
return custom_container <S> (s);
}int main () {
std::string t = "asdf";
auto s = make_container(t);

for (char c : s) {
std::cout << c;
}
}

Выходы

BEGINENDasdf

6

Другие решения

N3337 6.5.4 / 1:

(…) начать-выр а также конец выраж определяются следующим образом:

— если _RangeT тип массива, начать-выр а также конец выраж являются
__range а также __range + __boundсоответственно (…);

— если _RangeT это тип класса, unquali фи эд-идентификаторs begin а также end являются
посмотрел в поле зрения класса _RangeT как будто по доступу члена класса
lookup (3.4.5), и если любой (или оба) находит хотя бы одно объявление,
начать-выр а также конец выраж являются __range.begin() а также __range.end(),
соответственно;

— иначе, начать-выр а также конец выраж являются begin(__range) а также
end(__range)соответственно где begin а также end смотрят с
зависимый от аргумента поиск (3.4.2). Для целей этого имени
поиск, пространство имен std это связанное пространство имен.

Другими словами, это будет называть std::string«s begin а также end функции-члены (второй список). Правильное решение — предоставить класс-оболочку как Энтониответ подсказывает.

Примечание: если вы используете -std=c++1y Вы можете опустить завершающий тип decl.

Вы также можете написать typedef, чтобы сделать его менее набирающим:

typedef custom_string<std::string> cs;

for (char c : cs(t)) {
std::cout << c;
}
6

Самый чистый способ сделать это, по крайней мере, на этапе использования, это разметить ваш тип с целью специальной итерации.

Сначала немного техники:

template<class Mark, class T>
struct marked_type {
T raw;
marked_type(T&& in):raw(std::forward<T>(in)) {}
};
template<typename Mark, typename T>
marked_type<Mark, T> mark_type( T&& t ) {
return {std::forward<T>(t)};
}

затем мы изобретаем метку, которая говорит «странно повторяться», и начинаем перегрузку begin / end:

struct strange_iteration {};
template<typename T>
auto begin( marked_type<strange_iteration, T> const& container )
-> decltype( std::begin(std::forward<T>(container.raw)) )
{
std::cout << "BEGIN";
using std::begin;
return begin(std::forward<T>(container.raw));
}
template<typename T>
auto end( marked_type<strange_iteration, T> const& container )
-> decltype( std::end(std::forward<T>(container.raw)) )
{
std::cout << "END";
using std::end;
return end(std::forward<T>(container.raw));
}

а затем в точке использования:

std::string s = "hello world";
for( char c : mark_type<strange_iteration>(s) ) {
std::cout << c;
}
std::cout << "\n";

с одной запиской, которую я написал mark_type быть слишком общим.

Сейчас, mark_type<Foo> создаст ссылки на lvalues ​​и создаст перенесенную копию значения rvalue, если оно будет передано ему. На итерации время жизни возвращаемого значения будет увеличено путем увеличения продолжительности ссылки.

Вы можете использовать эту технику, чтобы сделать такие вещи, как

for( char c : mark_type<reverse_iteration>(s) )

где теперь мы вместо этого выполняем итерацию в обратном направлении, независимо от контейнера, который мы передали. «Создание копии» для rvalue необходимо для таких конструкций:

for( char c: mark_type<reverse_iteration>(mark_type<strange_iteration>(s))

где мы цепочки марки. Продление срока службы применяется только к самому внешнему возвращаемому значению, и наше «создание копии и перемещение» по значению r в основном является ручным продлением срока службы.

Наконец, std::begin Использование в вышеприведенном коде лучше сделать в контексте ADL-допущения в возвращаемых значениях. Создайте пространство имен помощника следующим образом:

namespace adl_helper {
using std::begin; using std::end;
template<typename T>
auto adl_begin(T&& t)->decltype( begin(std::forward<T>(t)) ); // no implementation
template<typename T>
auto adl_end(T&& t)->decltype( end(std::forward<T>(t)) ); // no implementation
// add adl_cbegin, adl_rbegin etc in C++14
}

затем заменить std::begin в decltypeS в моем коде выше с adl_helper::adl_begin, который подражает как for( a:b ) петли найти begin а также end немного лучше (не идеально, но лучше).

C ++ 1y может поставляться с некоторыми механизмами для устранения необходимости вышеупомянутого взлома.

Пример кода работает: http://ideone.com/RYvzD0

1
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector