Каковы преимущества использования boost::any_range
?
Вот пример:
typedef boost::any_range<
int
, boost::forward_traversal_tag
, int
, std::ptrdiff_t
> integer_range;
void display_integers(const integer_range& rng)
{
boost::copy(rng,
std::ostream_iterator<int>(std::cout, ","));
std::cout << std::endl;
}
int main(){
std::vector<int> input{ ... };
std::list<int> input2{ ... };
display_integers(input);
display_integers(input2);
}
Но та же функциональность с большей эффективностью может быть достигнута с помощью параметра шаблона, который удовлетворяет концепции ForwardRange:
template <class ForwardRange>
void display_integers(const ForwardRange& rng)
{
boost::copy(rng,
std::ostream_iterator<int>(std::cout, ","));
std::cout << std::endl;
}
Поэтому я ищу сценарии, когда стоит использовать any_range. Может быть, я что-то упустил.
Эта техника называется Type Erasure. Есть полная статья с описанием плюсов и минусов на примере any_iterator
: О напряженности между объектно-ориентированным и общим программированием в C ++.
Можно скрыть (в отдельном файле / библиотеке) реализацию / определение
void display_integers(const integer_range& rng)
Но в случае
template <class ForwardRange>
void display_integers(const ForwardRange& rng)
Вы должны предоставить исходный код пользователям (или, по крайней мере, сделать явные экземпляры где-то).
Более того, в первом случае display_integers
будет скомпилирован только один раз, но во втором он будет скомпилирован для каждого типа пройденного диапазона.
Кроме того, вы можете иметь где-то
integer_range rng;
и в течение жизни rng
Вы можете назначить диапазоны разные типы к нему:
vector<int> v;
list<int> l;
integer_range rng;
rng = v;
rng = l;
Самым большим недостатком стирания типа является его стоимость во время выполнения; все операции являются виртуальными и не могут быть встроены (легко).
Постскриптум Другой известный пример стирания типа станд :: функция
boost::any_range
может использоваться для возврата диапазонов из функций. Представьте себе следующий пример:
auto make_range(std::vector<int> v) -> decltype(???)
{
return v | filter([](int x){ return x % 2 == 0;})
| transform([](int x){ return x * 2;});
}
*: gcc не компилирует вышеперечисленное, не оборачивая его std::function
, косилка лязг 3.2 работает путем прямого прохождения лямбда
Очень трудно узнать, что возвращается из этой функции. Также, лямбда и decltype не работают вместе, поэтому мы не можем вывести тип, используя decltype
при прохождении только лямбда. Одним из решений является использование boost::any_range
как в вашем примере (другой обходной путь должен использовать std::function
как указано Евгений Панасюк в комментариях):
integer_range make_range(std::vector<int> v)
{
return v | filter([](int x){ return x % 2 == 0;})
| transform([](int x){ return x * 2;});
}
Рабочий пример с gcc с помощью std::function
,
Рабочий пример с лязгом прохождение лямбды напрямую.