Warning, file /include/oneapi/tbb/parallel_for_each.h was not indexed
or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017 #ifndef __TBB_parallel_for_each_H
0018 #define __TBB_parallel_for_each_H
0019
0020 #include "detail/_config.h"
0021 #include "detail/_namespace_injection.h"
0022 #include "detail/_exception.h"
0023 #include "detail/_task.h"
0024 #include "detail/_aligned_space.h"
0025 #include "detail/_small_object_pool.h"
0026 #include "detail/_utils.h"
0027
0028 #include "parallel_for.h"
0029 #include "task_group.h" // task_group_context
0030
0031 #include <iterator>
0032 #include <type_traits>
0033
0034 namespace tbb {
0035 namespace detail {
0036 #if __TBB_CPP20_CONCEPTS_PRESENT
0037 namespace d1 {
0038 template <typename Item>
0039 class feeder;
0040
0041 }
0042 inline namespace d0 {
0043
0044 template <typename Body, typename ItemType, typename FeederItemType>
0045 concept parallel_for_each_body = std::invocable<const std::remove_reference_t<Body>&, ItemType&&> ||
0046 std::invocable<const std::remove_reference_t<Body>&, ItemType&&, tbb::detail::d1::feeder<FeederItemType>&>;
0047
0048 }
0049 #endif
0050 namespace d2 {
0051 template<typename Body, typename Item> class feeder_impl;
0052 }
0053
0054 namespace d1 {
0055
0056 template<typename Item>
0057 class feeder {
0058 feeder() {}
0059 feeder(const feeder&) = delete;
0060 void operator=( const feeder&) = delete;
0061
0062 virtual ~feeder () {}
0063 virtual void internal_add_copy(const Item& item) = 0;
0064 virtual void internal_add_move(Item&& item) = 0;
0065
0066 template<typename Body_, typename Item_> friend class d2::feeder_impl;
0067 public:
0068
0069 void add(const Item& item) {internal_add_copy(item);}
0070 void add(Item&& item) {internal_add_move(std::move(item));}
0071 };
0072
0073 }
0074
0075 namespace d2 {
0076 using namespace tbb::detail::d1;
0077
0078
0079 template<class Body>
0080 struct parallel_for_each_operator_selector {
0081 public:
0082 template<typename ItemArg, typename FeederArg>
0083 static auto call(const Body& body, ItemArg&& item, FeederArg*)
0084 -> decltype(tbb::detail::invoke(body, std::forward<ItemArg>(item)), void()) {
0085 #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
0086
0087 #pragma warning (push)
0088 #pragma warning (disable: 4239)
0089 #endif
0090
0091 tbb::detail::invoke(body, std::forward<ItemArg>(item));
0092
0093 #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
0094 #pragma warning (pop)
0095 #endif
0096 }
0097
0098 template<typename ItemArg, typename FeederArg>
0099 static auto call(const Body& body, ItemArg&& item, FeederArg* feeder)
0100 -> decltype(tbb::detail::invoke(body, std::forward<ItemArg>(item), *feeder), void()) {
0101 #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
0102
0103 #pragma warning (push)
0104 #pragma warning (disable: 4239)
0105 #endif
0106 __TBB_ASSERT(feeder, "Feeder was not created but should be");
0107
0108 tbb::detail::invoke(body, std::forward<ItemArg>(item), *feeder);
0109
0110 #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
0111 #pragma warning (pop)
0112 #endif
0113 }
0114 };
0115
0116 template<typename Body, typename Item>
0117 struct feeder_item_task: public task {
0118 using feeder_type = feeder_impl<Body, Item>;
0119
0120 template <typename ItemType>
0121 feeder_item_task(ItemType&& input_item, feeder_type& feeder, small_object_allocator& alloc) :
0122 item(std::forward<ItemType>(input_item)),
0123 my_feeder(feeder),
0124 my_allocator(alloc)
0125 {}
0126
0127 void finalize(const execution_data& ed) {
0128 my_feeder.my_wait_context.release();
0129 my_allocator.delete_object(this, ed);
0130 }
0131
0132
0133
0134 using first_priority = int;
0135 using second_priority = double;
0136
0137 template <typename BodyType, typename ItemType, typename FeederType>
0138 static auto call(const BodyType& call_body, ItemType& call_item, FeederType& call_feeder, first_priority)
0139 -> decltype(parallel_for_each_operator_selector<Body>::call(call_body, std::move(call_item), &call_feeder), void())
0140 {
0141 parallel_for_each_operator_selector<Body>::call(call_body, std::move(call_item), &call_feeder);
0142 }
0143
0144 template <typename BodyType, typename ItemType, typename FeederType>
0145 static void call(const BodyType& call_body, ItemType& call_item, FeederType& call_feeder, second_priority) {
0146 parallel_for_each_operator_selector<Body>::call(call_body, call_item, &call_feeder);
0147 }
0148
0149 task* execute(execution_data& ed) override {
0150 call(my_feeder.my_body, item, my_feeder, first_priority{});
0151 finalize(ed);
0152 return nullptr;
0153 }
0154
0155 task* cancel(execution_data& ed) override {
0156 finalize(ed);
0157 return nullptr;
0158 }
0159
0160 Item item;
0161 feeder_type& my_feeder;
0162 small_object_allocator my_allocator;
0163 };
0164
0165
0166
0167 template<typename Body, typename Item>
0168 class feeder_impl : public feeder<Item> {
0169
0170 void internal_add_copy_impl(std::true_type, const Item& item) {
0171 using feeder_task = feeder_item_task<Body, Item>;
0172 small_object_allocator alloc;
0173 auto task = alloc.new_object<feeder_task>(item, *this, alloc);
0174
0175 my_wait_context.reserve();
0176 spawn(*task, my_execution_context);
0177 }
0178
0179 void internal_add_copy_impl(std::false_type, const Item&) {
0180 __TBB_ASSERT(false, "Overloading for r-value reference doesn't work or it's not movable and not copyable object");
0181 }
0182
0183 void internal_add_copy(const Item& item) override {
0184 internal_add_copy_impl(typename std::is_copy_constructible<Item>::type(), item);
0185 }
0186
0187 void internal_add_move(Item&& item) override {
0188 using feeder_task = feeder_item_task<Body, Item>;
0189 small_object_allocator alloc{};
0190 auto task = alloc.new_object<feeder_task>(std::move(item), *this, alloc);
0191
0192 my_wait_context.reserve();
0193 spawn(*task, my_execution_context);
0194 }
0195 public:
0196 feeder_impl(const Body& body, wait_context& w_context, task_group_context &context)
0197 : my_body(body),
0198 my_wait_context(w_context)
0199 , my_execution_context(context)
0200 {}
0201
0202 const Body& my_body;
0203 wait_context& my_wait_context;
0204 task_group_context& my_execution_context;
0205 };
0206
0207
0208
0209 template<typename Iterator, typename Body, typename Item>
0210 struct for_each_iteration_task: public task {
0211 using feeder_type = feeder_impl<Body, Item>;
0212
0213 for_each_iteration_task(Iterator input_item_ptr, const Body& body, feeder_impl<Body, Item>* feeder_ptr, wait_context& wait_context) :
0214 item_ptr(input_item_ptr), my_body(body), my_feeder_ptr(feeder_ptr), parent_wait_context(wait_context)
0215 {}
0216
0217 void finalize() {
0218 parent_wait_context.release();
0219 }
0220
0221 task* execute(execution_data&) override {
0222 parallel_for_each_operator_selector<Body>::call(my_body, *item_ptr, my_feeder_ptr);
0223 finalize();
0224 return nullptr;
0225 }
0226
0227 task* cancel(execution_data&) override {
0228 finalize();
0229 return nullptr;
0230 }
0231
0232 Iterator item_ptr;
0233 const Body& my_body;
0234 feeder_impl<Body, Item>* my_feeder_ptr;
0235 wait_context& parent_wait_context;
0236 };
0237
0238
0239
0240 template <typename Body, typename Item, typename = void>
0241 struct input_iteration_task_iterator_helper {
0242
0243
0244 using type = const Item*;
0245 };
0246
0247 template <typename Body, typename Item>
0248 struct input_iteration_task_iterator_helper<Body, Item,
0249 tbb::detail::void_t<decltype(parallel_for_each_operator_selector<Body>::call(std::declval<const Body&>(),
0250 std::declval<Item&&>(),
0251 std::declval<feeder_impl<Body, Item>*>()))>>
0252 {
0253 using type = std::move_iterator<Item*>;
0254 };
0255
0256
0257
0258 template <typename Body, typename Item>
0259 struct input_block_handling_task : public task {
0260 static constexpr size_t max_block_size = 4;
0261
0262 using feeder_type = feeder_impl<Body, Item>;
0263 using iteration_task_iterator_type = typename input_iteration_task_iterator_helper<Body, Item>::type;
0264 using iteration_task = for_each_iteration_task<iteration_task_iterator_type, Body, Item>;
0265
0266 input_block_handling_task(wait_context& root_wait_context, task_group_context& e_context,
0267 const Body& body, feeder_impl<Body, Item>* feeder_ptr, small_object_allocator& alloc)
0268 :my_size(0), my_wait_context(0), my_root_wait_context(root_wait_context),
0269 my_execution_context(e_context), my_allocator(alloc)
0270 {
0271 auto item_it = block_iteration_space.begin();
0272 for (auto* it = task_pool.begin(); it != task_pool.end(); ++it) {
0273 new (it) iteration_task(iteration_task_iterator_type(item_it++), body, feeder_ptr, my_wait_context);
0274 }
0275 }
0276
0277 void finalize(const execution_data& ed) {
0278 my_root_wait_context.release();
0279 my_allocator.delete_object(this, ed);
0280 }
0281
0282 task* execute(execution_data& ed) override {
0283 __TBB_ASSERT( my_size > 0, "Negative size was passed to task");
0284 for (std::size_t counter = 1; counter < my_size; ++counter) {
0285 my_wait_context.reserve();
0286 spawn(*(task_pool.begin() + counter), my_execution_context);
0287 }
0288 my_wait_context.reserve();
0289 execute_and_wait(*task_pool.begin(), my_execution_context,
0290 my_wait_context, my_execution_context);
0291
0292
0293 finalize(ed);
0294 return nullptr;
0295 }
0296
0297 task* cancel(execution_data& ed) override {
0298 finalize(ed);
0299 return nullptr;
0300 }
0301
0302 ~input_block_handling_task() {
0303 for(std::size_t counter = 0; counter < max_block_size; ++counter) {
0304 (task_pool.begin() + counter)->~iteration_task();
0305 if (counter < my_size) {
0306 (block_iteration_space.begin() + counter)->~Item();
0307 }
0308 }
0309 }
0310
0311 aligned_space<Item, max_block_size> block_iteration_space;
0312 aligned_space<iteration_task, max_block_size> task_pool;
0313 std::size_t my_size;
0314 wait_context my_wait_context;
0315 wait_context& my_root_wait_context;
0316 task_group_context& my_execution_context;
0317 small_object_allocator my_allocator;
0318 };
0319
0320
0321
0322 template <typename Iterator, typename Body, typename Item>
0323 struct forward_block_handling_task : public task {
0324 static constexpr size_t max_block_size = 4;
0325
0326 using iteration_task = for_each_iteration_task<Iterator, Body, Item>;
0327
0328 forward_block_handling_task(Iterator first, std::size_t size,
0329 wait_context& w_context, task_group_context& e_context,
0330 const Body& body, feeder_impl<Body, Item>* feeder_ptr,
0331 small_object_allocator& alloc)
0332 : my_size(size), my_wait_context(0), my_root_wait_context(w_context),
0333 my_execution_context(e_context), my_allocator(alloc)
0334 {
0335 auto* task_it = task_pool.begin();
0336 for (std::size_t i = 0; i < size; i++) {
0337 new (task_it++) iteration_task(first, body, feeder_ptr, my_wait_context);
0338 ++first;
0339 }
0340 }
0341
0342 void finalize(const execution_data& ed) {
0343 my_root_wait_context.release();
0344 my_allocator.delete_object(this, ed);
0345 }
0346
0347 task* execute(execution_data& ed) override {
0348 __TBB_ASSERT( my_size > 0, "Negative size was passed to task");
0349 for(std::size_t counter = 1; counter < my_size; ++counter) {
0350 my_wait_context.reserve();
0351 spawn(*(task_pool.begin() + counter), my_execution_context);
0352 }
0353 my_wait_context.reserve();
0354 execute_and_wait(*task_pool.begin(), my_execution_context,
0355 my_wait_context, my_execution_context);
0356
0357
0358 finalize(ed);
0359 return nullptr;
0360 }
0361
0362 task* cancel(execution_data& ed) override {
0363 finalize(ed);
0364 return nullptr;
0365 }
0366
0367 ~forward_block_handling_task() {
0368 for(std::size_t counter = 0; counter < my_size; ++counter) {
0369 (task_pool.begin() + counter)->~iteration_task();
0370 }
0371 }
0372
0373 aligned_space<iteration_task, max_block_size> task_pool;
0374 std::size_t my_size;
0375 wait_context my_wait_context;
0376 wait_context& my_root_wait_context;
0377 task_group_context& my_execution_context;
0378 small_object_allocator my_allocator;
0379 };
0380
0381
0382
0383
0384 template <typename Iterator, typename Body, typename Item>
0385 class parallel_for_body_wrapper {
0386 Iterator my_first;
0387 const Body& my_body;
0388 feeder_impl<Body, Item>* my_feeder_ptr;
0389 public:
0390 parallel_for_body_wrapper(Iterator first, const Body& body, feeder_impl<Body, Item>* feeder_ptr)
0391 : my_first(first), my_body(body), my_feeder_ptr(feeder_ptr) {}
0392
0393 void operator()(tbb::blocked_range<std::size_t> range) const {
0394 #if __INTEL_COMPILER
0395 #pragma ivdep
0396 #endif
0397 for (std::size_t count = range.begin(); count != range.end(); count++) {
0398 parallel_for_each_operator_selector<Body>::call(my_body, *(my_first + count),
0399 my_feeder_ptr);
0400 }
0401 }
0402 };
0403
0404
0405
0406
0407 template<typename It>
0408 using tag = typename std::iterator_traits<It>::iterator_category;
0409
0410 #if __TBB_CPP20_PRESENT
0411 template <typename It>
0412 struct move_iterator_dispatch_helper {
0413 using type = It;
0414 };
0415
0416
0417
0418
0419
0420 template <typename It>
0421 struct move_iterator_dispatch_helper<std::move_iterator<It>> {
0422 using type = It;
0423 };
0424
0425 template <typename It>
0426 using iterator_tag_dispatch_impl =
0427 std::conditional_t<std::random_access_iterator<It>,
0428 std::random_access_iterator_tag,
0429 std::conditional_t<std::forward_iterator<It>,
0430 std::forward_iterator_tag,
0431 std::input_iterator_tag>>;
0432
0433 template <typename It>
0434 using iterator_tag_dispatch =
0435 iterator_tag_dispatch_impl<typename move_iterator_dispatch_helper<It>::type>;
0436
0437 #else
0438 template<typename It>
0439 using iterator_tag_dispatch = typename
0440 std::conditional<
0441 std::is_base_of<std::random_access_iterator_tag, tag<It>>::value,
0442 std::random_access_iterator_tag,
0443 typename std::conditional<
0444 std::is_base_of<std::forward_iterator_tag, tag<It>>::value,
0445 std::forward_iterator_tag,
0446 std::input_iterator_tag
0447 >::type
0448 >::type;
0449 #endif
0450
0451 template <typename Body, typename Iterator, typename Item>
0452 using feeder_is_required = tbb::detail::void_t<decltype(tbb::detail::invoke(std::declval<const Body>(),
0453 std::declval<typename std::iterator_traits<Iterator>::reference>(),
0454 std::declval<feeder<Item>&>()))>;
0455
0456
0457 template <typename Iterator, typename Body, typename Item, typename = void>
0458 struct feeder_holder {
0459 feeder_holder( wait_context&, task_group_context&, const Body& ) {}
0460
0461 feeder_impl<Body, Item>* feeder_ptr() { return nullptr; }
0462 };
0463
0464 template <typename Iterator, typename Body, typename Item>
0465 class feeder_holder<Iterator, Body, Item, feeder_is_required<Body, Iterator, Item>> {
0466 public:
0467 feeder_holder( wait_context& w_context, task_group_context& context, const Body& body )
0468 : my_feeder(body, w_context, context) {}
0469
0470 feeder_impl<Body, Item>* feeder_ptr() { return &my_feeder; }
0471 private:
0472 feeder_impl<Body, Item> my_feeder;
0473 };
0474
0475 template <typename Iterator, typename Body, typename Item>
0476 class for_each_root_task_base : public task {
0477 public:
0478 for_each_root_task_base(Iterator first, Iterator last, const Body& body, wait_context& w_context, task_group_context& e_context)
0479 : my_first(first), my_last(last), my_wait_context(w_context), my_execution_context(e_context),
0480 my_body(body), my_feeder_holder(my_wait_context, my_execution_context, my_body)
0481 {
0482 my_wait_context.reserve();
0483 }
0484 private:
0485 task* cancel(execution_data&) override {
0486 this->my_wait_context.release();
0487 return nullptr;
0488 }
0489 protected:
0490 Iterator my_first;
0491 Iterator my_last;
0492 wait_context& my_wait_context;
0493 task_group_context& my_execution_context;
0494 const Body& my_body;
0495 feeder_holder<Iterator, Body, Item> my_feeder_holder;
0496 };
0497
0498
0499
0500
0501 template <typename Iterator, typename Body, typename Item, typename IteratorTag = iterator_tag_dispatch<Iterator>>
0502 class for_each_root_task : public for_each_root_task_base<Iterator, Body, Item>
0503 {
0504 using base_type = for_each_root_task_base<Iterator, Body, Item>;
0505 public:
0506 using base_type::base_type;
0507 private:
0508 task* execute(execution_data& ed) override {
0509 using block_handling_type = input_block_handling_task<Body, Item>;
0510
0511 if (this->my_first == this->my_last) {
0512 this->my_wait_context.release();
0513 return nullptr;
0514 }
0515
0516 this->my_wait_context.reserve();
0517 small_object_allocator alloc{};
0518 auto block_handling_task = alloc.new_object<block_handling_type>(ed, this->my_wait_context, this->my_execution_context,
0519 this->my_body, this->my_feeder_holder.feeder_ptr(),
0520 alloc);
0521
0522 auto* block_iterator = block_handling_task->block_iteration_space.begin();
0523 for (; !(this->my_first == this->my_last) && block_handling_task->my_size < block_handling_type::max_block_size; ++this->my_first) {
0524
0525 new (block_iterator++) Item(*this->my_first);
0526 ++block_handling_task->my_size;
0527 }
0528
0529
0530 spawn(*this, this->my_execution_context);
0531 return block_handling_task;
0532 }
0533 };
0534
0535
0536
0537
0538 template <typename Iterator, typename Body, typename Item>
0539 class for_each_root_task<Iterator, Body, Item, std::forward_iterator_tag>
0540 : public for_each_root_task_base<Iterator, Body, Item>
0541 {
0542 using base_type = for_each_root_task_base<Iterator, Body, Item>;
0543 public:
0544 using base_type::base_type;
0545 private:
0546 task* execute(execution_data& ed) override {
0547 using block_handling_type = forward_block_handling_task<Iterator, Body, Item>;
0548 if (this->my_first == this->my_last) {
0549 this->my_wait_context.release();
0550 return nullptr;
0551 }
0552
0553 std::size_t block_size{0};
0554 Iterator first_block_element = this->my_first;
0555 for (; !(this->my_first == this->my_last) && block_size < block_handling_type::max_block_size; ++this->my_first) {
0556 ++block_size;
0557 }
0558
0559 this->my_wait_context.reserve();
0560 small_object_allocator alloc{};
0561 auto block_handling_task = alloc.new_object<block_handling_type>(ed, first_block_element, block_size,
0562 this->my_wait_context, this->my_execution_context,
0563 this->my_body, this->my_feeder_holder.feeder_ptr(), alloc);
0564
0565
0566 spawn(*this, this->my_execution_context);
0567 return block_handling_task;
0568 }
0569 };
0570
0571
0572
0573
0574 template <typename Iterator, typename Body, typename Item>
0575 class for_each_root_task<Iterator, Body, Item, std::random_access_iterator_tag>
0576 : public for_each_root_task_base<Iterator, Body, Item>
0577 {
0578 using base_type = for_each_root_task_base<Iterator, Body, Item>;
0579 public:
0580 using base_type::base_type;
0581 private:
0582 task* execute(execution_data&) override {
0583 tbb::parallel_for(
0584 tbb::blocked_range<std::size_t>(0, std::distance(this->my_first, this->my_last)),
0585 parallel_for_body_wrapper<Iterator, Body, Item>(this->my_first, this->my_body, this->my_feeder_holder.feeder_ptr())
0586 , this->my_execution_context
0587 );
0588
0589 this->my_wait_context.release();
0590 return nullptr;
0591 }
0592 };
0593
0594
0595
0596
0597 template<typename Body, typename Item, typename FeederArg>
0598 auto feeder_argument_parser(void (Body::*)(Item, feeder<FeederArg>&) const) -> FeederArg;
0599
0600 template<typename Body, typename>
0601 decltype(feeder_argument_parser<Body>(&Body::operator())) get_item_type_impl(int);
0602 template<typename Body, typename Item> Item get_item_type_impl(...);
0603
0604 template <typename Body, typename Item>
0605 using get_item_type = decltype(get_item_type_impl<Body, Item>(0));
0606
0607 #if __TBB_CPP20_CONCEPTS_PRESENT
0608 template <typename Body, typename ItemType>
0609 using feeder_item_type = std::remove_cvref_t<get_item_type<Body, ItemType>>;
0610
0611 template <typename Body, typename Iterator>
0612 concept parallel_for_each_iterator_body =
0613 parallel_for_each_body<Body, iterator_reference_type<Iterator>, feeder_item_type<Body, iterator_reference_type<Iterator>>>;
0614
0615 template <typename Body, typename Range>
0616 concept parallel_for_each_range_body =
0617 parallel_for_each_body<Body, range_reference_type<Range>, feeder_item_type<Body, range_reference_type<Range>>>;
0618 #endif
0619
0620
0621
0622 template<typename Iterator, typename Body>
0623 void run_parallel_for_each( Iterator first, Iterator last, const Body& body, task_group_context& context)
0624 {
0625 if (!(first == last)) {
0626 using ItemType = get_item_type<Body, typename std::iterator_traits<Iterator>::value_type>;
0627 wait_context w_context(0);
0628
0629 for_each_root_task<Iterator, Body, ItemType> root_task(first, last, body, w_context, context);
0630
0631 execute_and_wait(root_task, context, w_context, context);
0632 }
0633 }
0634
0635
0636
0637
0638
0639
0640
0641
0642
0643
0644
0645
0646
0647
0648
0649
0650
0651
0652
0653
0654
0655
0656
0657
0658
0659 template<typename Iterator, typename Body>
0660 __TBB_requires(std::input_iterator<Iterator> && parallel_for_each_iterator_body<Body, Iterator>)
0661 void parallel_for_each(Iterator first, Iterator last, const Body& body) {
0662 task_group_context context(PARALLEL_FOR_EACH);
0663 run_parallel_for_each<Iterator, Body>(first, last, body, context);
0664 }
0665
0666 template<typename Range, typename Body>
0667 __TBB_requires(container_based_sequence<Range, std::input_iterator_tag> && parallel_for_each_range_body<Body, Range>)
0668 void parallel_for_each(Range& rng, const Body& body) {
0669 parallel_for_each(std::begin(rng), std::end(rng), body);
0670 }
0671
0672 template<typename Range, typename Body>
0673 __TBB_requires(container_based_sequence<Range, std::input_iterator_tag> && parallel_for_each_range_body<Body, Range>)
0674 void parallel_for_each(const Range& rng, const Body& body) {
0675 parallel_for_each(std::begin(rng), std::end(rng), body);
0676 }
0677
0678
0679
0680 template<typename Iterator, typename Body>
0681 __TBB_requires(std::input_iterator<Iterator> && parallel_for_each_iterator_body<Body, Iterator>)
0682 void parallel_for_each(Iterator first, Iterator last, const Body& body, task_group_context& context) {
0683 run_parallel_for_each<Iterator, Body>(first, last, body, context);
0684 }
0685
0686 template<typename Range, typename Body>
0687 __TBB_requires(container_based_sequence<Range, std::input_iterator_tag> && parallel_for_each_range_body<Body, Range>)
0688 void parallel_for_each(Range& rng, const Body& body, task_group_context& context) {
0689 parallel_for_each(std::begin(rng), std::end(rng), body, context);
0690 }
0691
0692 template<typename Range, typename Body>
0693 __TBB_requires(container_based_sequence<Range, std::input_iterator_tag> && parallel_for_each_range_body<Body, Range>)
0694 void parallel_for_each(const Range& rng, const Body& body, task_group_context& context) {
0695 parallel_for_each(std::begin(rng), std::end(rng), body, context);
0696 }
0697
0698 }
0699 }
0700
0701
0702
0703 inline namespace v1 {
0704 using detail::d2::parallel_for_each;
0705 using detail::d1::feeder;
0706 }
0707
0708 }
0709
0710 #endif